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>
36#if QT_CONFIG(qml_network)
37#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
38#include <QtNetwork/qnetworkreply.h>
39#include <QtNetwork/qsslerror.h>
42#include <private/qdebug_p.h>
44#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8
47#define CACHE_EXPIRE_TIME 30
50#define CACHE_REMOVAL_FRACTION 4
52#define PIXMAP_PROFILE(Code) Q_QUICK_PROFILE(QQuickProfiler::ProfilePixmapCache, Code)
54#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
55# define USE_THREADED_DOWNLOAD 1
57# define USE_THREADED_DOWNLOAD 0
62using namespace Qt::Literals::StringLiterals;
64#if defined(QT_DEBUG) && QT_CONFIG(thread)
65class ThreadAffinityMarker
68 ThreadAffinityMarker() { attachToCurrentThread(); }
70 void assertOnAssignedThread()
72 QMutexLocker locker(&m_mutex);
73 if (!m_assignedThread)
74 attachToCurrentThread();
75 Q_ASSERT_X(m_assignedThread == QThread::currentThreadId(), Q_FUNC_INFO,
76 "Running on a wrong thread!");
79 void detachFromCurrentThread()
81 QMutexLocker locker(&m_mutex);
82 m_assignedThread =
nullptr;
85 void attachToCurrentThread() { m_assignedThread = QThread::currentThreadId(); }
88 Qt::HANDLE m_assignedThread;
91# define Q_THREAD_AFFINITY_MARKER(x) ThreadAffinityMarker x
92# define Q_ASSERT_CALLED_ON_VALID_THREAD(x) x.assertOnAssignedThread()
93# define Q_DETACH_THREAD_AFFINITY_MARKER(x) x.detachFromCurrentThread()
95# define Q_THREAD_AFFINITY_MARKER(x)
96# define Q_ASSERT_CALLED_ON_VALID_THREAD(x)
97# define Q_DETACH_THREAD_AFFINITY_MARKER(x)
100const QLatin1String QQuickPixmap::itemGrabberScheme = QLatin1String(
"itemgrabber");
105
106
107
117 return url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1);
120QQuickDefaultTextureFactory::QQuickDefaultTextureFactory(
const QImage &image)
122 if (image.format() == QImage::Format_ARGB32_Premultiplied ||
123 image.format() == QImage::Format_RGB32 ||
124 image.format() == QImage::Format_RGBA16FPx4_Premultiplied ||
125 image.format() == QImage::Format_RGBA16FPx4 ||
126 image.format() == QImage::Format_RGBX16FPx4 ||
127 image.format() == QImage::Format_RGBA32FPx4_Premultiplied ||
128 image.format() == QImage::Format_RGBA32FPx4 ||
129 image.format() == QImage::Format_RGBX32FPx4) {
132 im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
138QSGTexture *QQuickDefaultTextureFactory::createTexture(QQuickWindow *window)
const
140 static bool transient = qEnvironmentVariableIsSet(
"QSG_TRANSIENT_IMAGES");
141 QMutexLocker locker(&mutex);
144 QSGTexture *t = window->createTextureFromImage(im, QQuickWindow::TextureCanUseAtlas);
145 if (transient && t) {
152QImage QQuickDefaultTextureFactory::image()
const
154 QMutexLocker locker(&mutex);
158bool QQuickDefaultTextureFactory::isImageDropped()
const
160 QMutexLocker locker(&mutex);
214
215
265 void processJob(
QQuickPixmapReply *,
const QUrl &,
const QString &, QQuickImageProvider::ImageType,
const QSharedPointer<QQuickImageProvider> &);
266#if QT_CONFIG(qml_network)
269 void asyncResponseFinished(QQuickImageResponse *);
271 QList<QQuickPixmapReply*> jobs;
272 QList<QQuickPixmapReply *> cancelledJobs;
275#if QT_CONFIG(quick_pixmap_cache_threaded_download)
277
278
287
288
291 return ownedReaderThreadExecutionEnforcer.get();
293 std::unique_ptr<ReaderThreadExecutionEnforcer> ownedReaderThreadExecutionEnforcer;
296#if QT_CONFIG(qml_network)
301 QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses;
306 static int replyDownloadProgressMethodIndex;
307 static int replyFinishedMethodIndex;
308 static int downloadProgressMethodIndex;
309 static int threadNetworkRequestDoneMethodIndex;
310 static QHash<QQmlEngine *,QQuickPixmapReader*> readers;
315#if QT_CONFIG(quick_pixmap_cache_threaded_download)
316# define PIXMAP_READER_LOCK() QMutexLocker locker(&mutex)
318# define PIXMAP_READER_LOCK()
321class QQuickPixmapCache;
324
325
330 const QQuickImageProviderOptions &po,
const QString &e)
342 QQuickPixmapData(
const QUrl &u,
const QRect &r,
const QSize &s,
const QQuickImageProviderOptions &po,
343 QQuickImageProviderOptions::AutoTransform aTransform,
int frame=0,
int frameCount=1, qreal dpr = 1)
356 const QSize &s,
const QRect &r,
const QSize &rs,
const QQuickImageProviderOptions &po,
357 QQuickImageProviderOptions::AutoTransform aTransform,
int frame=0,
int frameCount=1, qreal dpr = 1)
379 requestSize = implicitSize = texture->textureSize();
384 delete textureFactory;
389 void release(QQuickPixmapCache *store =
nullptr);
395 auto *factory = qobject_cast<QQuickDefaultTextureFactory *>(textureFactory);
396 return factory && factory->isImageDropped();
451 const QSize &implicitSize, QQuickTextureFactory *factory)
454 QCoreApplication::postEvent(
this,
new Event(error, errorString, implicitSize, factory));
464 delete textureFactory;
467#if QT_CONFIG(qml_network)
468QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
470 if (!accessManager) {
471 Q_ASSERT(readerThreadExecutionEnforcer());
472 accessManager = QQmlTypeLoader::get(engine)->createNetworkAccessManager(
473 readerThreadExecutionEnforcer());
475 return accessManager;
482 if (image->hasAlphaChannel() && image->data_ptr()
483 && !image->data_ptr()->checkForAlphaPixels()) {
484 switch (image->format()) {
485 case QImage::Format_RGBA8888:
486 case QImage::Format_RGBA8888_Premultiplied:
487 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX8888, Qt::AutoColor))
490 *image = image->convertToFormat(QImage::Format_RGBX8888);
492 case QImage::Format_A2BGR30_Premultiplied:
493 if (image->data_ptr()->convertInPlace(QImage::Format_BGR30, Qt::AutoColor))
496 *image = image->convertToFormat(QImage::Format_BGR30);
498 case QImage::Format_A2RGB30_Premultiplied:
499 if (image->data_ptr()->convertInPlace(QImage::Format_RGB30, Qt::AutoColor))
502 *image = image->convertToFormat(QImage::Format_RGB30);
504 case QImage::Format_RGBA16FPx4:
505 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX16FPx4, Qt::AutoColor))
508 *image = image->convertToFormat(QImage::Format_RGBX16FPx4);
510 case QImage::Format_RGBA32FPx4:
511 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX32FPx4, Qt::AutoColor))
514 *image = image->convertToFormat(QImage::Format_RGBX32FPx4);
517 if (image->data_ptr()->convertInPlace(QImage::Format_RGB32, Qt::AutoColor))
520 *image = image->convertToFormat(QImage::Format_RGB32);
526static bool readImage(
const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
int *frameCount,
527 const QRect &requestRegion,
const QSize &requestSize,
const QQuickImageProviderOptions &providerOptions,
528 QQuickImageProviderOptions::AutoTransform *appliedTransform =
nullptr,
int frame = 0,
529 qreal devicePixelRatio = 1.0)
531 QImageReader imgio(dev);
532 if (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform)
533 imgio.setAutoTransform(providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform);
534 else if (appliedTransform)
535 *appliedTransform = imgio.autoTransform() ? QQuickImageProviderOptions::ApplyTransform : QQuickImageProviderOptions::DoNotApplyTransform;
537 if (frame < imgio.imageCount())
538 imgio.jumpToImage(frame);
541 *frameCount = imgio.imageCount();
543 QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions, devicePixelRatio);
544 if (scSize.isValid())
545 imgio.setScaledSize(scSize);
546 if (!requestRegion.isNull())
547 imgio.setScaledClipRect(requestRegion);
548 const QSize originalSize = imgio.size();
549 qCDebug(lcImg) << url <<
"frame" << frame <<
"of" << imgio.imageCount()
550 <<
"requestRegion" << requestRegion <<
"QImageReader size" << originalSize <<
"-> scSize" << scSize;
553 *impsize = originalSize;
555 if (imgio.read(image)) {
556 maybeRemoveAlpha(image);
557 if (impsize && impsize->width() < 0)
558 *impsize = image->size();
559 if (providerOptions.targetColorSpace().isValid()) {
560 if (image->colorSpace().isValid())
561 image->convertToColorSpace(providerOptions.targetColorSpace());
563 image->setColorSpace(providerOptions.targetColorSpace());
568 *errorString = QQuickPixmap::tr(
"Error decoding: %1: %2").arg(url.toString())
569 .arg(imgio.errorString());
577 res.reserve(list.size());
578 for (
const QByteArray &item : list)
579 res.append(QString::fromLatin1(item));
588 delete QSGContext::createTextureFactoryFromImage(QImage());
589 hasOpenGL = QQuickWindow::sceneGraphBackend().isEmpty();
590 QList<QByteArray> list;
592 list.append(QSGTextureReader::supportedFileFormats());
593 list.append(QImageReader::supportedImageFormats());
594 fileSuffixes = fromLatin1List(list);
606 QFileInfo fi(localFile);
607 if (!fi.suffix().isEmpty() || fi.exists())
610 QString tryFile = localFile + QStringLiteral(
".xxxx");
611 const int suffixIdx = localFile.size() + 1;
612 for (
const QString &suffix : backendSupport()->fileSuffixes) {
613 tryFile.replace(suffixIdx, 10, suffix);
614 if (QFileInfo::exists(tryFile))
621: QThread(eng), engine(eng)
622#if QT_CONFIG(qml_network)
623, accessManager(
nullptr)
629 engine->handle()->typeLoader();
632#if QT_CONFIG(quick_pixmap_cache_threaded_download)
633 start(QThread::LowestPriority);
644 readers.remove(engine);
645 readerMutex.unlock();
650 for (QQuickPixmapReply *reply : std::as_const(jobs)) {
651 if (reply->data && reply->data->reply == reply)
652 reply->data->reply =
nullptr;
658 cancelledJobs.append(reply);
662#if QT_CONFIG(qml_network)
663 for (
auto *reply : std::as_const(networkJobs))
666 for (
auto *reply : std::as_const(asyncResponses))
668#if !QT_CONFIG(quick_pixmap_cache_threaded_download)
674 if (readerThreadExecutionEnforcer())
679#if QT_CONFIG(quick_pixmap_cache_threaded_download)
692#if QT_CONFIG(qml_network)
693 for (QQuickPixmapReply *reply : std::as_const(networkJobs))
696 for (QQuickPixmapReply *reply : std::as_const(asyncResponses))
699#if QT_CONFIG(qml_network)
702 asyncResponses.clear();
705#if QT_CONFIG(qml_network)
706void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
708 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
710 QQuickPixmapReply *job = networkJobs.take(reply);
714 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
717 QQuickTextureFactory *factory =
nullptr;
718 if (reply->error()) {
719 error = QQuickPixmapReply::Loading;
720 errorString = reply->errorString();
722 QByteArray all = reply->readAll();
724 buff.open(QIODevice::ReadOnly);
725 QSGTextureReader texReader(&buff, reply->url().fileName());
726 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
727 factory = texReader.read();
729 readSize = factory->textureSize();
731 error = QQuickPixmapReply::Decoding;
732 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(reply->url().toString());
736 int const frame = job->data ? job->data->frame : 0;
737 const qreal dpr = job->data ? job->data->devicePixelRatio : 1;
738 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount,
739 job->requestRegion, job->requestSize, job->providerOptions,
nullptr, frame, dpr))
740 error = QQuickPixmapReply::Decoding;
742 job->data->frameCount = frameCount;
747 factory = QQuickTextureFactory::textureFactoryForImage(image);
749 PIXMAP_READER_LOCK();
750 if (!cancelledJobs.contains(job))
751 job->postReply(error, errorString, readSize, factory);
753 reply->deleteLater();
756 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
767 QQuickTextureFactory *t =
nullptr;
768 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
770 if (!response->errorString().isEmpty()) {
771 error = QQuickPixmapReply::Loading;
772 errorString = response->errorString();
774 t = response->textureFactory();
778 if (!cancelledJobs.contains(job))
779 job->postReply(error, errorString, t ? t->textureSize() : QSize(), t);
783 response->deleteLater();
793 QCoreApplication::postEvent(
794 this,
new QEvent(QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs)));
800 case QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs):
801 reader->processJobs();
804 return QObject::event(e);
810#if QT_CONFIG(qml_network)
811 QNetworkReply *reply =
static_cast<QNetworkReply *>(sender());
812 reader->networkRequestDone(reply);
818 reader->asyncResponseFinished(response);
823 QQuickImageResponse *response =
static_cast<QQuickImageResponse *>(sender());
833 if (cancelledJobs.isEmpty() && jobs.isEmpty())
837 if (!cancelledJobs.isEmpty()) {
838 for (
int i = 0; i < cancelledJobs.size(); ++i) {
840#if QT_CONFIG(qml_network)
841 QNetworkReply *reply = networkJobs.key(job, 0);
843 networkJobs.remove(reply);
844 if (reply->isRunning()) {
851 QQuickImageResponse *asyncResponse = asyncResponses.key(job);
853 asyncResponses.remove(asyncResponse);
854 asyncResponse->cancel();
857 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url));
861 cancelledJobs.clear();
864 if (!jobs.isEmpty()) {
866 bool usableJob =
false;
867 for (
int i = jobs.size() - 1; !usableJob && i >= 0; i--) {
869 const QUrl url = job->url;
871 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
872 QSharedPointer<QQuickImageProvider> provider;
874 if (url.scheme() == QLatin1String(
"image")) {
875 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
876 provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>();
878 imageType = provider->imageType();
882 localFile = QQmlFile::urlToLocalFileOrQrc(url);
885 if (localFile.isEmpty() && url.scheme() == QLatin1String(
"content"))
886 localFile = url.toString();
887 usableJob = !localFile.isEmpty()
888#if QT_CONFIG(qml_network)
889 || networkJobs.size() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
899 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
901#if QT_CONFIG(quick_pixmap_cache_threaded_download)
903 auto relockMutexGuard = qScopeGuard(([&locker]() {
907 processJob(job, url, localFile, imageType, provider);
918 QQuickImageProvider::ImageType imageType,
const QSharedPointer<QQuickImageProvider> &provider)
923 if (url.scheme() == QLatin1String(
"image")) {
927 if (imageType == QQuickImageProvider::Invalid) {
928 QString errorStr = QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString());
930 if (!cancelledJobs.contains(runningJob))
931 runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize,
nullptr);
936 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
939 case QQuickImageProvider::Invalid:
945 case QQuickImageProvider::Image:
949 image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
951 image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize);
953 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
955 if (image.isNull()) {
956 errorCode = QQuickPixmapReply::Loading;
957 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
960 if (!cancelledJobs.contains(runningJob)) {
961 runningJob->postReply(errorCode, errorStr, readSize,
962 QQuickTextureFactory::textureFactoryForImage(image));
967 case QQuickImageProvider::Pixmap:
971 pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
973 pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize);
975 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
977 if (pixmap.isNull()) {
978 errorCode = QQuickPixmapReply::Loading;
979 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
983 if (!cancelledJobs.contains(runningJob)) {
984 runningJob->postReply(
985 errorCode, errorStr, readSize,
986 QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()));
991 case QQuickImageProvider::Texture:
993 QQuickTextureFactory *t;
995 t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
997 t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize);
999 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
1002 errorCode = QQuickPixmapReply::Loading;
1003 errorStr = QQuickPixmap::tr(
"Failed to get texture from provider: %1").arg(url.toString());
1006 if (!cancelledJobs.contains(runningJob))
1007 runningJob->postReply(errorCode, errorStr, readSize, t);
1013 case QQuickImageProvider::ImageResponse:
1015 QQuickImageResponse *response;
1017 response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions);
1019 QQuickAsyncImageProvider *asyncProvider =
static_cast<QQuickAsyncImageProvider*>(provider.get());
1020 response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
1024 QObject::connect(response, &QQuickImageResponse::finished, readerThreadExecutionEnforcer(),
1025 qOverload<>(&ReaderThreadExecutionEnforcer::asyncResponseFinished));
1028 auto provider_copy = provider;
1029 QObject::connect(response, &QQuickImageResponse::destroyed, response, [provider_copy]() {
1037 if (
static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
1038 QMetaObject::invokeMethod(readerThreadExecutionEnforcer(),
"asyncResponseFinished",
1039 Qt::QueuedConnection,
1040 Q_ARG(QQuickImageResponse *, response));
1043 asyncResponses.insert(response, runningJob);
1049 if (!localFile.isEmpty()) {
1052 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
1057 auto specialDevice = runningJob
->data->specialDevice;
1058 if (specialDevice.isNull() || QObjectPrivate::get(specialDevice.data())->deleteLaterCalled) {
1059 qCDebug(lcImg) <<
"readImage job aborted" << url;
1066 if (!specialDevice->thread()) {
1067 qCDebug(lcQsgLeak) << specialDevice.data() <<
": changing thread affinity so that"
1068 << QThread::currentThread() <<
"will handle any deleteLater() calls";
1069 specialDevice->moveToThread(QThread::currentThread());
1071 if (!readImage(url, specialDevice.data(), &image, &errorStr, &readSize, &frameCount,
1072 runningJob->requestRegion, runningJob->requestSize,
1073 runningJob->providerOptions,
nullptr, runningJob->data->frame,
1074 runningJob->data->devicePixelRatio)) {
1075 errorCode = QQuickPixmapReply::Loading;
1076 }
else if (runningJob
->data) {
1080 QFile f(existingImageFileForPath(localFile));
1081 if (f.open(QIODevice::ReadOnly)) {
1082 QSGTextureReader texReader(&f, localFile);
1083 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1084 QQuickTextureFactory *factory = texReader.read();
1086 readSize = factory->textureSize();
1088 errorStr = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1089 if (f.fileName() != localFile)
1090 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1091 errorCode = QQuickPixmapReply::Decoding;
1094 if (!cancelledJobs.contains(runningJob))
1095 runningJob->postReply(errorCode, errorStr, readSize, factory);
1100 const qreal dpr = runningJob
->data ? runningJob
->data->devicePixelRatio : 1;
1101 if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
1102 runningJob->requestRegion, runningJob->requestSize,
1103 runningJob->providerOptions,
nullptr, frame, dpr)) {
1104 errorCode = QQuickPixmapReply::Loading;
1105 if (f.fileName() != localFile)
1106 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1107 }
else if (runningJob
->data) {
1112 errorStr = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1113 errorCode = QQuickPixmapReply::Loading;
1117 if (!cancelledJobs.contains(runningJob)) {
1118 runningJob->postReply(errorCode, errorStr, readSize,
1119 QQuickTextureFactory::textureFactoryForImage(image));
1122#if QT_CONFIG(qml_network)
1124 QNetworkRequest req(url);
1125 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
1126 QNetworkReply *reply = networkAccessManager()->get(req);
1128 QMetaObject::connect(reply, replyDownloadProgressMethodIndex, runningJob,
1129 downloadProgressMethodIndex);
1130 QMetaObject::connect(reply, replyFinishedMethodIndex, readerThreadExecutionEnforcer(),
1131 threadNetworkRequestDoneMethodIndex);
1133 networkJobs.insert(reply, runningJob);
1147 readers.insert(engine, reader);
1156 return readers.value(engine, 0);
1162 reply->engineForReader = engine;
1170 if (readerThreadExecutionEnforcer())
1178 cancelledJobs.append(reply);
1181 if (readerThreadExecutionEnforcer())
1186 if (jobs.removeAll(reply) == 0) {
1187 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(reply->url));
1197 if (replyDownloadProgressMethodIndex == -1) {
1198#if QT_CONFIG(qml_network)
1199 replyDownloadProgressMethodIndex =
1200 QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
1201 replyFinishedMethodIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
1202 const QMetaObject *ir = &ReaderThreadExecutionEnforcer::staticMetaObject;
1203 threadNetworkRequestDoneMethodIndex = ir->indexOfSlot(
"networkRequestDone()");
1205 downloadProgressMethodIndex =
1206 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1209#if QT_CONFIG(quick_pixmap_cache_threaded_download)
1210 const auto guard = qScopeGuard([
this]() {
1212 PIXMAP_READER_LOCK();
1213 delete runLoopReaderThreadExecutionEnforcer;
1214 runLoopReaderThreadExecutionEnforcer =
nullptr;
1218 PIXMAP_READER_LOCK();
1219 Q_ASSERT(!runLoopReaderThreadExecutionEnforcer);
1220 runLoopReaderThreadExecutionEnforcer =
new ReaderThreadExecutionEnforcer(
this);
1226 ownedReaderThreadExecutionEnforcer = std::make_unique<ReaderThreadExecutionEnforcer>(
this);
1231inline bool operator==(
const QQuickPixmapKey &lhs,
const QQuickPixmapKey &rhs)
1233 return *lhs.url == *rhs.url &&
1234 *lhs.region == *rhs.region &&
1235 *lhs.size == *rhs.size &&
1236 lhs.frame == rhs.frame &&
1237 lhs.options == rhs.options;
1242 return qHashMulti(seed, *key.url, *key.region, *key.size, key.frame, key.options.autoTransform());
1245#ifndef QT_NO_DEBUG_STREAM
1248 QDebugStateSaver saver(debug);
1251 debug <<
"QQuickPixmapKey(0)";
1255 debug <<
"QQuickPixmapKey(" << key.url->toString() <<
" frame=" << key.frame;
1256 if (!key.region->isEmpty()) {
1257 debug <<
" region=";
1258 QtDebugUtils::formatQRect(debug, *key.region);
1260 if (!key.size->isEmpty()) {
1262 QtDebugUtils::formatQSize(debug, *key.size);
1270
1271
1272
1273
1274
1275QQuickPixmapCache::QQuickPixmapCache()
1277 if (
auto *app = QCoreApplication::instance(); app && thread() != app->thread())
1278 moveToThread(app->thread());
1281QQuickPixmapCache *QQuickPixmapCache::instance()
1283 static QQuickPixmapCache self;
1287QQuickPixmapCache::~QQuickPixmapCache()
1293
1294
1295
1296
1297
1298
1299
1300int QQuickPixmapCache::destroyCache()
1305 m_destroying =
true;
1313 int leakedPixmaps = 0;
1314 const auto cache = m_cache;
1315 for (
auto *pixmap : cache) {
1316 auto currRefCount = pixmap->refCount;
1319 qCDebug(lcQsgLeak) <<
"leaked pixmap: refCount" << pixmap->refCount << pixmap->url <<
"frame" << pixmap->frame
1320 <<
"size" << pixmap->requestSize <<
"region" << pixmap->requestRegion;
1321 while (currRefCount > 0) {
1322 pixmap->release(
this);
1329 while (m_lastUnreferencedPixmap)
1332 qCDebug(lcQsgLeak,
"Number of leaked pixmaps: %i", leakedPixmaps);
1333 return leakedPixmaps;
1336qsizetype QQuickPixmapCache::referencedCost()
const
1339 QMutexLocker locker(&m_cacheMutex);
1340 for (
const auto *pixmap : std::as_const(m_cache)) {
1341 if (pixmap->refCount)
1342 ret += pixmap->cost();
1348
1349
1350
1351
1352
1353
1354
1355void QQuickPixmapCache::unreferencePixmap(QQuickPixmapData *data)
1357 Q_ASSERT(data->prevUnreferenced ==
nullptr);
1358 Q_ASSERT(data->prevUnreferencedPtr ==
nullptr);
1359 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1361 data->nextUnreferenced = m_unreferencedPixmaps;
1362 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
1363 if (!m_destroying) {
1364 m_unreferencedCost += data->cost();
1365 qCDebug(lcImg) << data->url <<
"had cost" << data->cost() <<
"of total unreferenced" << m_unreferencedCost;
1368 m_unreferencedPixmaps = data;
1369 if (m_unreferencedPixmaps->nextUnreferenced) {
1370 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
1371 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
1374 if (!m_lastUnreferencedPixmap)
1375 m_lastUnreferencedPixmap = data;
1380 if (thread() == QThread::currentThread())
1381 maybeStartExpiryTimer();
1382 else if (m_timerId == -1)
1383 QMetaObject::invokeMethod(
this, &QQuickPixmapCache::maybeStartExpiryTimer, Qt::QueuedConnection);
1387
1388
1389
1390void QQuickPixmapCache::maybeStartExpiryTimer()
1392 Q_ASSERT(thread() == QThread::currentThread());
1393 if (m_timerId == -1 && m_unreferencedPixmaps
1394 && !m_destroying && !QCoreApplication::closingDown()) {
1400
1401
1402
1403void QQuickPixmapCache::referencePixmap(QQuickPixmapData *data)
1405 Q_ASSERT(data->prevUnreferencedPtr);
1407 *data->prevUnreferencedPtr = data->nextUnreferenced;
1408 if (data->nextUnreferenced) {
1409 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
1410 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
1412 if (m_lastUnreferencedPixmap == data)
1413 m_lastUnreferencedPixmap = data->prevUnreferenced;
1415 data->nextUnreferenced =
nullptr;
1416 data->prevUnreferencedPtr =
nullptr;
1417 data->prevUnreferenced =
nullptr;
1419 m_unreferencedCost -= data->cost();
1420 qCDebug(lcImg) << data->url <<
"subtracts cost" << data->cost() <<
"of total" << m_unreferencedCost;
1424
1425
1426
1427void QQuickPixmapCache::shrinkCache(
int remove)
1429 qCDebug(lcImg) <<
"reduce unreferenced cost" << m_unreferencedCost <<
"to less than limit" << cache_limit;
1430 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
1431 QQuickPixmapData *data = m_lastUnreferencedPixmap;
1432 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1434 *data->prevUnreferencedPtr =
nullptr;
1435 m_lastUnreferencedPixmap = data->prevUnreferenced;
1436 data->prevUnreferencedPtr =
nullptr;
1437 data->prevUnreferenced =
nullptr;
1439 if (!m_destroying) {
1440 remove -= data->cost();
1441 m_unreferencedCost -= data->cost();
1443 data->removeFromCache(
this);
1448void QQuickPixmapCache::timerEvent(QTimerEvent *)
1452 shrinkCache(removalCost);
1454 if (m_unreferencedPixmaps ==
nullptr) {
1455 killTimer(m_timerId);
1460void QQuickPixmapCache::purgeCache()
1462 shrinkCache(m_unreferencedCost);
1465void QQuickPixmap::purgeCache()
1467 QQuickPixmapCache::instance()->purgeCache();
1474 if (finishedMethodIndex == -1) {
1475 finishedMethodIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex();
1477 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1489 if (event->type() == QEvent::User) {
1493 data->pixmapStatus = (de->error == NoError) ? QQuickPixmap::Ready : QQuickPixmap::Error;
1494 if (data->pixmapStatus == QQuickPixmap::Ready) {
1495 data->textureFactory = de->textureFactory;
1496 de->textureFactory =
nullptr;
1497 data->implicitSize = de->implicitSize;
1499 data->textureFactory !=
nullptr &&
data->textureFactory->textureSize().isValid() ?
1500 data->textureFactory->textureSize() :
1501 (
data->requestSize.isValid() ?
data->requestSize :
data->implicitSize)));
1503 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(data->url));
1504 data->errorString = de->errorString;
1511 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1517 return QObject::event(event);
1524 return textureFactory->textureByteCount();
1531 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1532 if (prevUnreferencedPtr)
1533 QQuickPixmapCache::instance()->referencePixmap(
this);
1540 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1546 QQuickPixmapReader::readerMutex.lock();
1547 QQuickPixmapReader *reader = QQuickPixmapReader::existingInstance(cancelReply->engineForReader);
1550 QQuickPixmapReader::readerMutex.unlock();
1553 store = store ? store : QQuickPixmapCache::instance();
1554 if (pixmapStatus == QQuickPixmap::Ready
1560 store->unreferencePixmap(
this);
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1584 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1585 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1586 if (lcImg().isDebugEnabled()) {
1587 qCDebug(lcImg) <<
"adding" << key <<
"to total" << QQuickPixmapCache::instance()->m_cache.size();
1588 for (
auto it = QQuickPixmapCache::instance()->m_cache.keyBegin(); it != QQuickPixmapCache::instance()->m_cache.keyEnd(); ++it) {
1589 if (*(it->url) == url && it->frame == frame)
1590 qCDebug(lcImg) <<
" similar pre-existing:" << *it;
1593 QQuickPixmapCache::instance()->m_cache.insert(key,
this);
1595 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1596 url, QQuickPixmapCache::instance()->m_cache.size()));
1604 store = QQuickPixmapCache::instance();
1605 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1606 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1607 store->m_cache.remove(key);
1608 qCDebug(lcImg) <<
"removed" << key << implicitSize <<
"; total remaining" << QQuickPixmapCache::instance()->m_cache.size();
1610 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1611 url, store->m_cache.size()));
1616 const QRect &requestRegion,
const QSize &requestSize,
1617 const QQuickImageProviderOptions &providerOptions,
int frame,
bool *ok,
1618 qreal devicePixelRatio)
1620 if (url.scheme() == QLatin1String(
"image")) {
1623 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
1624 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1625 QSharedPointer<QQuickImageProvider> provider = enginePrivate->imageProvider(imageProviderId(url)).objectCast<QQuickImageProvider>();
1627 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
1629 imageType = provider->imageType();
1631 switch (imageType) {
1632 case QQuickImageProvider::Invalid:
1633 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1634 QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString()));
1635 case QQuickImageProvider::Texture:
1637 QQuickTextureFactory *texture = providerV2 ? providerV2->requestTexture(imageId(url), &readSize, requestSize, providerOptions)
1638 : provider->requestTexture(imageId(url), &readSize, requestSize);
1641 return new QQuickPixmapData(url, texture, readSize, requestRegion, requestSize,
1642 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1647 case QQuickImageProvider::Image:
1649 QImage image = providerV2 ? providerV2->requestImage(imageId(url), &readSize, requestSize, providerOptions)
1650 : provider->requestImage(imageId(url), &readSize, requestSize);
1651 if (!image.isNull()) {
1653 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image),
1654 readSize, requestRegion, requestSize, providerOptions,
1655 QQuickImageProviderOptions::UsePluginDefaultTransform,
1656 frame, 1, devicePixelRatio);
1660 case QQuickImageProvider::Pixmap:
1662 QPixmap pixmap = providerV2 ? providerV2->requestPixmap(imageId(url), &readSize, requestSize, providerOptions)
1663 : provider->requestPixmap(imageId(url), &readSize, requestSize);
1664 if (!pixmap.isNull()) {
1666 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()),
1667 readSize, requestRegion, requestSize, providerOptions,
1668 QQuickImageProviderOptions::UsePluginDefaultTransform,
1669 frame, 1, devicePixelRatio);
1673 case QQuickImageProvider::ImageResponse:
1676 Q_ASSERT(imageType != QQuickImageProvider::ImageResponse &&
"Sync call to ImageResponse provider");
1681 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1682 QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString()));
1685 QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
1686 if (localFile.isEmpty())
1689 QFile f(existingImageFileForPath(localFile));
1691 QString errorString;
1693 if (f.open(QIODevice::ReadOnly)) {
1694 QSGTextureReader texReader(&f, localFile);
1695 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1696 QQuickTextureFactory *factory = texReader.read();
1699 return new QQuickPixmapData(url, factory, factory->textureSize(), requestRegion, requestSize,
1700 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1702 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1703 if (f.fileName() != localFile)
1704 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1708 QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
1710 if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
1711 providerOptions, &appliedTransform, frame, devicePixelRatio)) {
1713 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
1714 providerOptions, appliedTransform, frame, frameCount);
1715 }
else if (f.fileName() != localFile) {
1716 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1720 errorString = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1722 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, errorString);
1733QQuickPixmap::QQuickPixmap()
1738QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url)
1744QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url, Options options)
1747 load(engine, url, options);
1750QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url,
const QRect ®ion,
const QSize &size)
1753 load(engine, url, region, size);
1756QQuickPixmap::QQuickPixmap(
const QUrl &url,
const QImage &image)
1758 d =
new QQuickPixmapData(url,
new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
1759 QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform,
1760 0, 1, image.devicePixelRatio());
1764QQuickPixmap::~QQuickPixmap()
1772bool QQuickPixmap::isNull()
const
1774 return d ==
nullptr;
1777bool QQuickPixmap::isReady()
const
1779 return status() == Ready;
1782bool QQuickPixmap::isError()
const
1784 return status() == Error;
1787bool QQuickPixmap::isLoading()
const
1789 return status() == Loading;
1792QString QQuickPixmap::error()
const
1795 return d->errorString;
1800QQuickPixmap::Status QQuickPixmap::status()
const
1803 return d->pixmapStatus;
1808const QUrl &QQuickPixmap::url()
const
1813 return nullPixmap()->url;
1816const QSize &QQuickPixmap::implicitSize()
const
1819 return d->implicitSize;
1821 return nullPixmap()->size;
1824const QSize &QQuickPixmap::requestSize()
const
1827 return d->requestSize;
1829 return nullPixmap()->size;
1832const QRect &QQuickPixmap::requestRegion()
const
1835 return d->requestRegion;
1837 return nullPixmap()->region;
1840QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform()
const
1843 return d->appliedTransform;
1845 return QQuickImageProviderOptions::UsePluginDefaultTransform;
1848int QQuickPixmap::frameCount()
const
1851 return d->frameCount;
1855QQuickTextureFactory *QQuickPixmap::textureFactory()
const
1858 return d->textureFactory;
1863bool QQuickPixmap::hasDroppedTransientImage()
const
1865 return d && d->hasDroppedTransientImage();
1868void QQuickPixmap::removeDroppedTransientImageFromCache()
1870 if (d && d->hasDroppedTransientImage()) {
1871 qCDebug(lcImg) <<
"removing pixmap with transient image data released from cache" << d->url;
1872 d->removeFromCache();
1876QImage QQuickPixmap::image()
const
1878 if (d && d->textureFactory)
1879 return d->textureFactory->image();
1883void QQuickPixmap::setImage(
const QImage &p)
1890 d =
new QQuickPixmapData(QQuickTextureFactory::textureFactoryForImage(p));
1894void QQuickPixmap::setPixmap(
const QQuickPixmap &other)
1908int QQuickPixmap::width()
const
1910 if (d && d->textureFactory)
1911 return d->textureFactory->textureSize().width();
1916int QQuickPixmap::height()
const
1918 if (d && d->textureFactory)
1919 return d->textureFactory->textureSize().height();
1924qreal QQuickPixmap::devicePixelRatio()
const
1926 return d ? d->devicePixelRatio : 1.0;
1929QRect QQuickPixmap::rect()
const
1931 if (d && d->textureFactory)
1932 return QRect(QPoint(), d->textureFactory->textureSize());
1937void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url)
1939 load(engine, url, QRect(), QSize(), QQuickPixmap::Cache);
1942void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url, QQuickPixmap::Options options)
1944 load(engine, url, QRect(), QSize(), options);
1947void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize)
1949 load(engine, url, requestRegion, requestSize, QQuickPixmap::Cache);
1952void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize, QQuickPixmap::Options options)
1954 load(engine, url, requestRegion, requestSize, options, QQuickImageProviderOptions());
1957void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
1958 QQuickPixmap::Options options,
const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount,
1959 qreal devicePixelRatio)
1966 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1967 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1969 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1970 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1973 QQuickPixmap::Options orgOptions = options;
1976 options |= QQuickPixmap::Cache;
1982 if (url.scheme() == itemGrabberScheme) {
1985 if (requestSize != dummySize)
1986 qWarning() <<
"Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead.";
1987 const QQuickPixmapKey grabberKey = { &url, &dummyRegion, &dummySize, 0, QQuickImageProviderOptions() };
1988 iter = store->m_cache.find(grabberKey);
1989 }
else if (options & QQuickPixmap::Cache)
1990 iter = store->m_cache.find(key);
1992 if (iter == store->m_cache.end()) {
1998 if (url.scheme() == QLatin1String(
"image")) {
1999 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
2000 if (
auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
2001 const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps);
2002 if (!threadedPixmaps && provider->imageType() == QQuickImageProvider::Pixmap) {
2004 options &= ~QQuickPixmap::Asynchronous;
2005 }
else if (provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading) {
2006 options |= QQuickPixmap::Asynchronous;
2011 if (!(options & QQuickPixmap::Asynchronous)) {
2013 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
2014 d = createPixmapDataSync(engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
2016 PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
2017 if (options & QQuickPixmap::Cache)
2020 d->storeToCache = orgOptions & QQuickPixmap::Cache;
2025 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
2030 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
2031 QQuickImageProviderOptions::UsePluginDefaultTransform, frame,
2032 frameCount, devicePixelRatio);
2033 if (options & QQuickPixmap::Cache)
2036 d->storeToCache = orgOptions & QQuickPixmap::Cache;
2039 QQuickPixmapReader::readerMutex.lock();
2040 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
2041 d->reply = reader->getImage(d);
2042 reader->startJob(d->reply);
2043 QQuickPixmapReader::readerMutex.unlock();
2047 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame;
2052
2053
2054
2055
2056
2057
2058void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device,
const QUrl &url,
2059 const QRect &requestRegion,
const QSize &requestSize,
2060 const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount)
2063 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
2064 QQuickPixmapCache *store = QQuickPixmapCache::instance();
2065 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
2066 QMutexLocker locker(&store->m_cacheMutex);
2067 iter = store->m_cache.find(key);
2068 if (iter == store->m_cache.end()) {
2073 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
2074 QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
2075 d->specialDevice = device;
2076 d->fromSpecialDevice =
true;
2079 QQuickPixmapReader::readerMutex.lock();
2080 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
2081 d->reply = reader->getImage(d);
2083 QObject::connect(d->reply, &QQuickPixmapReply::destroyed, store, [oldD]() {
2085 }, Qt::QueuedConnection);
2087 reader->startJob(d->reply);
2088 QQuickPixmapReader::readerMutex.unlock();
2092 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame <<
"refCount" << d->refCount;
2099void QQuickPixmap::clear()
2107void QQuickPixmap::clear(QObject *obj)
2111 QObject::disconnect(d->reply,
nullptr, obj,
nullptr);
2117bool QQuickPixmap::isCached(
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
2118 const int frame,
const QQuickImageProviderOptions &options)
2120 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, options };
2121 QQuickPixmapCache *store = QQuickPixmapCache::instance();
2123 return store->m_cache.contains(key);
2126bool QQuickPixmap::isScalableImageFormat(
const QUrl &url)
2128 if (url.scheme() ==
"image"_L1)
2131 const auto isScalableFormat = [](
const auto &f) {
2132 return (f ==
"svg"_L1 || f ==
"svgz"_L1 || f ==
"pdf"_L1);
2135 if (url.scheme() ==
"data"_L1) {
2137 const QString path = url.path(QUrl::PrettyDecoded);
2138 QStringView mediaType(path);
2139 if (
const qsizetype comma = mediaType.indexOf(u','); comma >= 0)
2140 mediaType.truncate(comma);
2141 if (
const qsizetype semicolon = mediaType.indexOf(u';'); semicolon >= 0)
2142 mediaType.truncate(semicolon);
2144 const QList<QByteArray> formats =
2145 QImageReader::imageFormatsForMimeType(mediaType.trimmed().toLatin1().toLower());
2146 return std::any_of(formats.cbegin(), formats.cend(), [&](
const QByteArray &format) {
2147 return isScalableFormat(QLatin1StringView(format));
2151 const QString suffix = QFileInfo(url.path(QUrl::PrettyDecoded)).suffix();
2152 if (!suffix.isEmpty())
2153 return isScalableFormat(suffix);
2157 const QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
2158 if (localFile.isEmpty())
2160 const QString file = existingImageFileForPath(localFile);
2161 const QString fileSuffix = QFileInfo(file).suffix();
2162 if (!fileSuffix.isEmpty())
2163 return isScalableFormat(fileSuffix);
2164 return isScalableFormat(QLatin1StringView(QImageReader::imageFormat(file)));
2167bool QQuickPixmap::connectFinished(QObject *object,
const char *method)
2169 if (!d || !d->reply) {
2170 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2174 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
2177bool QQuickPixmap::connectFinished(QObject *object,
int method)
2179 if (!d || !d->reply) {
2180 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2184 return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedMethodIndex, object, method);
2187bool QQuickPixmap::connectDownloadProgress(QObject *object,
const char *method)
2189 if (!d || !d->reply) {
2190 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2194 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object,
2198bool QQuickPixmap::connectDownloadProgress(QObject *object,
int method)
2200 if (!d || !d->reply) {
2201 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2205 return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressMethodIndex, object,
2209QColorSpace QQuickPixmap::colorSpace()
const
2211 if (!d || !d->textureFactory)
2212 return QColorSpace();
2213 return d->textureFactory->image().colorSpace();
2218#include <qquickpixmapcache.moc>
2220#include "moc_qquickpixmap_p.cpp"
2221#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
bool hasDroppedTransientImage() const
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()
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
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