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);
868 if (localFile.isEmpty() && url.scheme() == QLatin1String(
"content"))
869 localFile = url.toString();
870 usableJob = !localFile.isEmpty()
871#if QT_CONFIG(qml_network)
872 || networkJobs.size() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
882 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
884#if QT_CONFIG(quick_pixmap_cache_threaded_download)
886 auto relockMutexGuard = qScopeGuard(([&locker]() {
890 processJob(job, url, localFile, imageType, provider);
901 QQuickImageProvider::ImageType imageType,
const QSharedPointer<QQuickImageProvider> &provider)
906 if (url.scheme() == QLatin1String(
"image")) {
910 if (imageType == QQuickImageProvider::Invalid) {
911 QString errorStr = QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString());
913 if (!cancelledJobs.contains(runningJob))
914 runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize,
nullptr);
919 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
922 case QQuickImageProvider::Invalid:
928 case QQuickImageProvider::Image:
932 image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
934 image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize);
936 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
938 if (image.isNull()) {
939 errorCode = QQuickPixmapReply::Loading;
940 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
943 if (!cancelledJobs.contains(runningJob)) {
944 runningJob->postReply(errorCode, errorStr, readSize,
945 QQuickTextureFactory::textureFactoryForImage(image));
950 case QQuickImageProvider::Pixmap:
954 pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
956 pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize);
958 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
960 if (pixmap.isNull()) {
961 errorCode = QQuickPixmapReply::Loading;
962 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
966 if (!cancelledJobs.contains(runningJob)) {
967 runningJob->postReply(
968 errorCode, errorStr, readSize,
969 QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()));
974 case QQuickImageProvider::Texture:
976 QQuickTextureFactory *t;
978 t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
980 t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize);
982 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
985 errorCode = QQuickPixmapReply::Loading;
986 errorStr = QQuickPixmap::tr(
"Failed to get texture from provider: %1").arg(url.toString());
989 if (!cancelledJobs.contains(runningJob))
990 runningJob->postReply(errorCode, errorStr, readSize, t);
996 case QQuickImageProvider::ImageResponse:
998 QQuickImageResponse *response;
1000 response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions);
1002 QQuickAsyncImageProvider *asyncProvider =
static_cast<QQuickAsyncImageProvider*>(provider.get());
1003 response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
1007 QObject::connect(response, &QQuickImageResponse::finished, readerThreadExecutionEnforcer(),
1008 qOverload<>(&ReaderThreadExecutionEnforcer::asyncResponseFinished));
1011 auto provider_copy = provider;
1012 QObject::connect(response, &QQuickImageResponse::destroyed, response, [provider_copy]() {
1020 if (
static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
1021 QMetaObject::invokeMethod(readerThreadExecutionEnforcer(),
"asyncResponseFinished",
1022 Qt::QueuedConnection,
1023 Q_ARG(QQuickImageResponse *, response));
1026 asyncResponses.insert(response, runningJob);
1032 if (!localFile.isEmpty()) {
1035 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
1040 auto specialDevice = runningJob
->data->specialDevice;
1041 if (specialDevice.isNull() || QObjectPrivate::get(specialDevice.data())->deleteLaterCalled) {
1042 qCDebug(lcImg) <<
"readImage job aborted" << url;
1049 if (!specialDevice->thread()) {
1050 qCDebug(lcQsgLeak) << specialDevice.data() <<
": changing thread affinity so that"
1051 << QThread::currentThread() <<
"will handle any deleteLater() calls";
1052 specialDevice->moveToThread(QThread::currentThread());
1054 if (!readImage(url, specialDevice.data(), &image, &errorStr, &readSize, &frameCount,
1055 runningJob->requestRegion, runningJob->requestSize,
1056 runningJob->providerOptions,
nullptr, runningJob->data->frame,
1057 runningJob->data->devicePixelRatio)) {
1058 errorCode = QQuickPixmapReply::Loading;
1059 }
else if (runningJob
->data) {
1063 QFile f(existingImageFileForPath(localFile));
1064 if (f.open(QIODevice::ReadOnly)) {
1065 QSGTextureReader texReader(&f, localFile);
1066 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1067 QQuickTextureFactory *factory = texReader.read();
1069 readSize = factory->textureSize();
1071 errorStr = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1072 if (f.fileName() != localFile)
1073 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1074 errorCode = QQuickPixmapReply::Decoding;
1077 if (!cancelledJobs.contains(runningJob))
1078 runningJob->postReply(errorCode, errorStr, readSize, factory);
1083 const qreal dpr = runningJob
->data ? runningJob
->data->devicePixelRatio : 1;
1084 if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
1085 runningJob->requestRegion, runningJob->requestSize,
1086 runningJob->providerOptions,
nullptr, frame, dpr)) {
1087 errorCode = QQuickPixmapReply::Loading;
1088 if (f.fileName() != localFile)
1089 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1090 }
else if (runningJob
->data) {
1095 errorStr = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1096 errorCode = QQuickPixmapReply::Loading;
1100 if (!cancelledJobs.contains(runningJob)) {
1101 runningJob->postReply(errorCode, errorStr, readSize,
1102 QQuickTextureFactory::textureFactoryForImage(image));
1105#if QT_CONFIG(qml_network)
1107 QNetworkRequest req(url);
1108 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
1109 QNetworkReply *reply = networkAccessManager()->get(req);
1111 QMetaObject::connect(reply, replyDownloadProgressMethodIndex, runningJob,
1112 downloadProgressMethodIndex);
1113 QMetaObject::connect(reply, replyFinishedMethodIndex, readerThreadExecutionEnforcer(),
1114 threadNetworkRequestDoneMethodIndex);
1116 networkJobs.insert(reply, runningJob);
1130 readers.insert(engine, reader);
1139 return readers.value(engine, 0);
1145 reply->engineForReader = engine;
1153 if (readerThreadExecutionEnforcer())
1161 cancelledJobs.append(reply);
1164 if (readerThreadExecutionEnforcer())
1169 if (jobs.removeAll(reply) == 0) {
1170 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(reply->url));
1180 if (replyDownloadProgressMethodIndex == -1) {
1181#if QT_CONFIG(qml_network)
1182 replyDownloadProgressMethodIndex =
1183 QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
1184 replyFinishedMethodIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
1185 const QMetaObject *ir = &ReaderThreadExecutionEnforcer::staticMetaObject;
1186 threadNetworkRequestDoneMethodIndex = ir->indexOfSlot(
"networkRequestDone()");
1188 downloadProgressMethodIndex =
1189 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1192#if QT_CONFIG(quick_pixmap_cache_threaded_download)
1193 const auto guard = qScopeGuard([
this]() {
1195 PIXMAP_READER_LOCK();
1196 delete runLoopReaderThreadExecutionEnforcer;
1197 runLoopReaderThreadExecutionEnforcer =
nullptr;
1201 PIXMAP_READER_LOCK();
1202 Q_ASSERT(!runLoopReaderThreadExecutionEnforcer);
1203 runLoopReaderThreadExecutionEnforcer =
new ReaderThreadExecutionEnforcer(
this);
1209 ownedReaderThreadExecutionEnforcer = std::make_unique<ReaderThreadExecutionEnforcer>(
this);
1214inline bool operator==(
const QQuickPixmapKey &lhs,
const QQuickPixmapKey &rhs)
1216 return *lhs.url == *rhs.url &&
1217 *lhs.region == *rhs.region &&
1218 *lhs.size == *rhs.size &&
1219 lhs.frame == rhs.frame &&
1220 lhs.options == rhs.options;
1225 return qHashMulti(seed, *key.url, *key.region, *key.size, key.frame, key.options.autoTransform());
1228#ifndef QT_NO_DEBUG_STREAM
1231 QDebugStateSaver saver(debug);
1234 debug <<
"QQuickPixmapKey(0)";
1238 debug <<
"QQuickPixmapKey(" << key.url->toString() <<
" frame=" << key.frame;
1239 if (!key.region->isEmpty()) {
1240 debug <<
" region=";
1241 QtDebugUtils::formatQRect(debug, *key.region);
1243 if (!key.size->isEmpty()) {
1245 QtDebugUtils::formatQSize(debug, *key.size);
1252QQuickPixmapCache *QQuickPixmapCache::instance()
1254 static QQuickPixmapCache self;
1258QQuickPixmapCache::~QQuickPixmapCache()
1264
1265
1266
1267
1268
1269
1270
1271int QQuickPixmapCache::destroyCache()
1276 m_destroying =
true;
1284 int leakedPixmaps = 0;
1285 const auto cache = m_cache;
1286 for (
auto *pixmap : cache) {
1287 auto currRefCount = pixmap->refCount;
1290 qCDebug(lcQsgLeak) <<
"leaked pixmap: refCount" << pixmap->refCount << pixmap->url <<
"frame" << pixmap->frame
1291 <<
"size" << pixmap->requestSize <<
"region" << pixmap->requestRegion;
1292 while (currRefCount > 0) {
1293 pixmap->release(
this);
1300 while (m_lastUnreferencedPixmap)
1303 qCDebug(lcQsgLeak,
"Number of leaked pixmaps: %i", leakedPixmaps);
1304 return leakedPixmaps;
1307qsizetype QQuickPixmapCache::referencedCost()
const
1310 QMutexLocker locker(&m_cacheMutex);
1311 for (
const auto *pixmap : std::as_const(m_cache)) {
1312 if (pixmap->refCount)
1313 ret += pixmap->cost();
1319
1320
1321
1322void QQuickPixmapCache::unreferencePixmap(QQuickPixmapData *data)
1324 Q_ASSERT(data->prevUnreferenced ==
nullptr);
1325 Q_ASSERT(data->prevUnreferencedPtr ==
nullptr);
1326 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1328 data->nextUnreferenced = m_unreferencedPixmaps;
1329 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
1330 if (!m_destroying) {
1331 m_unreferencedCost += data->cost();
1332 qCDebug(lcImg) << data->url <<
"had cost" << data->cost() <<
"of total unreferenced" << m_unreferencedCost;
1335 m_unreferencedPixmaps = data;
1336 if (m_unreferencedPixmaps->nextUnreferenced) {
1337 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
1338 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
1341 if (!m_lastUnreferencedPixmap)
1342 m_lastUnreferencedPixmap = data;
1346 if (m_timerId == -1 && m_unreferencedPixmaps
1347 && !m_destroying && !QCoreApplication::closingDown()) {
1353
1354
1355
1356void QQuickPixmapCache::referencePixmap(QQuickPixmapData *data)
1358 Q_ASSERT(data->prevUnreferencedPtr);
1360 *data->prevUnreferencedPtr = data->nextUnreferenced;
1361 if (data->nextUnreferenced) {
1362 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
1363 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
1365 if (m_lastUnreferencedPixmap == data)
1366 m_lastUnreferencedPixmap = data->prevUnreferenced;
1368 data->nextUnreferenced =
nullptr;
1369 data->prevUnreferencedPtr =
nullptr;
1370 data->prevUnreferenced =
nullptr;
1372 m_unreferencedCost -= data->cost();
1373 qCDebug(lcImg) << data->url <<
"subtracts cost" << data->cost() <<
"of total" << m_unreferencedCost;
1377
1378
1379
1380void QQuickPixmapCache::shrinkCache(
int remove)
1382 qCDebug(lcImg) <<
"reduce unreferenced cost" << m_unreferencedCost <<
"to less than limit" << cache_limit;
1383 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
1384 QQuickPixmapData *data = m_lastUnreferencedPixmap;
1385 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1387 *data->prevUnreferencedPtr =
nullptr;
1388 m_lastUnreferencedPixmap = data->prevUnreferenced;
1389 data->prevUnreferencedPtr =
nullptr;
1390 data->prevUnreferenced =
nullptr;
1392 if (!m_destroying) {
1393 remove -= data->cost();
1394 m_unreferencedCost -= data->cost();
1396 data->removeFromCache(
this);
1401void QQuickPixmapCache::timerEvent(QTimerEvent *)
1405 shrinkCache(removalCost);
1407 if (m_unreferencedPixmaps ==
nullptr) {
1408 killTimer(m_timerId);
1413void QQuickPixmapCache::purgeCache()
1415 shrinkCache(m_unreferencedCost);
1418void QQuickPixmap::purgeCache()
1420 QQuickPixmapCache::instance()->purgeCache();
1427 if (finishedMethodIndex == -1) {
1428 finishedMethodIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex();
1429 downloadProgressMethodIndex =
1430 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1442 if (event->type() == QEvent::User) {
1446 data->pixmapStatus = (de->error == NoError) ? QQuickPixmap::Ready : QQuickPixmap::Error;
1447 if (data->pixmapStatus == QQuickPixmap::Ready) {
1448 data->textureFactory = de->textureFactory;
1449 de->textureFactory =
nullptr;
1450 data->implicitSize = de->implicitSize;
1452 data->textureFactory !=
nullptr &&
data->textureFactory->textureSize().isValid() ?
1453 data->textureFactory->textureSize() :
1454 (
data->requestSize.isValid() ?
data->requestSize :
data->implicitSize)));
1456 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(data->url));
1457 data->errorString = de->errorString;
1464 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1470 return QObject::event(event);
1477 return textureFactory->textureByteCount();
1484 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1485 if (prevUnreferencedPtr)
1486 QQuickPixmapCache::instance()->referencePixmap(
this);
1491 Q_ASSERT(refCount > 0);
1493 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1494 if (refCount == 0) {
1499 QQuickPixmapReader::readerMutex.lock();
1500 QQuickPixmapReader *reader = QQuickPixmapReader::existingInstance(cancelReply->engineForReader);
1503 QQuickPixmapReader::readerMutex.unlock();
1506 store = store ? store : QQuickPixmapCache::instance();
1507 if (pixmapStatus == QQuickPixmap::Ready
1513 store->unreferencePixmap(
this);
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1537 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1538 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1539 if (lcImg().isDebugEnabled()) {
1540 qCDebug(lcImg) <<
"adding" << key <<
"to total" << QQuickPixmapCache::instance()->m_cache.size();
1541 for (
auto it = QQuickPixmapCache::instance()->m_cache.keyBegin(); it != QQuickPixmapCache::instance()->m_cache.keyEnd(); ++it) {
1542 if (*(it->url) == url && it->frame == frame)
1543 qCDebug(lcImg) <<
" similar pre-existing:" << *it;
1546 QQuickPixmapCache::instance()->m_cache.insert(key,
this);
1548 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1549 url, QQuickPixmapCache::instance()->m_cache.size()));
1557 store = QQuickPixmapCache::instance();
1558 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1559 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1560 store->m_cache.remove(key);
1561 qCDebug(lcImg) <<
"removed" << key << implicitSize <<
"; total remaining" << QQuickPixmapCache::instance()->m_cache.size();
1563 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1564 url, store->m_cache.size()));
1569 const QRect &requestRegion,
const QSize &requestSize,
1570 const QQuickImageProviderOptions &providerOptions,
int frame,
bool *ok,
1571 qreal devicePixelRatio)
1573 if (url.scheme() == QLatin1String(
"image")) {
1576 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
1577 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1578 QSharedPointer<QQuickImageProvider> provider = enginePrivate->imageProvider(imageProviderId(url)).objectCast<QQuickImageProvider>();
1580 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
1582 imageType = provider->imageType();
1584 switch (imageType) {
1585 case QQuickImageProvider::Invalid:
1586 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1587 QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString()));
1588 case QQuickImageProvider::Texture:
1590 QQuickTextureFactory *texture = providerV2 ? providerV2->requestTexture(imageId(url), &readSize, requestSize, providerOptions)
1591 : provider->requestTexture(imageId(url), &readSize, requestSize);
1594 return new QQuickPixmapData(url, texture, readSize, requestRegion, requestSize,
1595 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1600 case QQuickImageProvider::Image:
1602 QImage image = providerV2 ? providerV2->requestImage(imageId(url), &readSize, requestSize, providerOptions)
1603 : provider->requestImage(imageId(url), &readSize, requestSize);
1604 if (!image.isNull()) {
1606 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image),
1607 readSize, requestRegion, requestSize, providerOptions,
1608 QQuickImageProviderOptions::UsePluginDefaultTransform,
1609 frame, 1, devicePixelRatio);
1613 case QQuickImageProvider::Pixmap:
1615 QPixmap pixmap = providerV2 ? providerV2->requestPixmap(imageId(url), &readSize, requestSize, providerOptions)
1616 : provider->requestPixmap(imageId(url), &readSize, requestSize);
1617 if (!pixmap.isNull()) {
1619 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()),
1620 readSize, requestRegion, requestSize, providerOptions,
1621 QQuickImageProviderOptions::UsePluginDefaultTransform,
1622 frame, 1, devicePixelRatio);
1626 case QQuickImageProvider::ImageResponse:
1629 Q_ASSERT(imageType != QQuickImageProvider::ImageResponse &&
"Sync call to ImageResponse provider");
1634 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1635 QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString()));
1638 QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
1639 if (localFile.isEmpty())
1642 QFile f(existingImageFileForPath(localFile));
1644 QString errorString;
1646 if (f.open(QIODevice::ReadOnly)) {
1647 QSGTextureReader texReader(&f, localFile);
1648 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1649 QQuickTextureFactory *factory = texReader.read();
1652 return new QQuickPixmapData(url, factory, factory->textureSize(), requestRegion, requestSize,
1653 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1655 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1656 if (f.fileName() != localFile)
1657 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1661 QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
1663 if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
1664 providerOptions, &appliedTransform, frame, devicePixelRatio)) {
1666 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
1667 providerOptions, appliedTransform, frame, frameCount);
1668 }
else if (f.fileName() != localFile) {
1669 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1673 errorString = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1675 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, errorString);
1686QQuickPixmap::QQuickPixmap()
1691QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url)
1697QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url, Options options)
1700 load(engine, url, options);
1703QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url,
const QRect ®ion,
const QSize &size)
1706 load(engine, url, region, size);
1709QQuickPixmap::QQuickPixmap(
const QUrl &url,
const QImage &image)
1711 d =
new QQuickPixmapData(url,
new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
1712 QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform);
1716QQuickPixmap::~QQuickPixmap()
1724bool QQuickPixmap::isNull()
const
1726 return d ==
nullptr;
1729bool QQuickPixmap::isReady()
const
1731 return status() == Ready;
1734bool QQuickPixmap::isError()
const
1736 return status() == Error;
1739bool QQuickPixmap::isLoading()
const
1741 return status() == Loading;
1744QString QQuickPixmap::error()
const
1747 return d->errorString;
1752QQuickPixmap::Status QQuickPixmap::status()
const
1755 return d->pixmapStatus;
1760const QUrl &QQuickPixmap::url()
const
1765 return nullPixmap()->url;
1768const QSize &QQuickPixmap::implicitSize()
const
1771 return d->implicitSize;
1773 return nullPixmap()->size;
1776const QSize &QQuickPixmap::requestSize()
const
1779 return d->requestSize;
1781 return nullPixmap()->size;
1784const QRect &QQuickPixmap::requestRegion()
const
1787 return d->requestRegion;
1789 return nullPixmap()->region;
1792QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform()
const
1795 return d->appliedTransform;
1797 return QQuickImageProviderOptions::UsePluginDefaultTransform;
1800int QQuickPixmap::frameCount()
const
1803 return d->frameCount;
1807QQuickTextureFactory *QQuickPixmap::textureFactory()
const
1810 return d->textureFactory;
1815QImage QQuickPixmap::image()
const
1817 if (d && d->textureFactory)
1818 return d->textureFactory->image();
1822void QQuickPixmap::setImage(
const QImage &p)
1829 d =
new QQuickPixmapData(QQuickTextureFactory::textureFactoryForImage(p));
1833void QQuickPixmap::setPixmap(
const QQuickPixmap &other)
1847int QQuickPixmap::width()
const
1849 if (d && d->textureFactory)
1850 return d->textureFactory->textureSize().width();
1855int QQuickPixmap::height()
const
1857 if (d && d->textureFactory)
1858 return d->textureFactory->textureSize().height();
1863QRect QQuickPixmap::rect()
const
1865 if (d && d->textureFactory)
1866 return QRect(QPoint(), d->textureFactory->textureSize());
1871void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url)
1873 load(engine, url, QRect(), QSize(), QQuickPixmap::Cache);
1876void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url, QQuickPixmap::Options options)
1878 load(engine, url, QRect(), QSize(), options);
1881void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize)
1883 load(engine, url, requestRegion, requestSize, QQuickPixmap::Cache);
1886void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize, QQuickPixmap::Options options)
1888 load(engine, url, requestRegion, requestSize, options, QQuickImageProviderOptions());
1891void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
1892 QQuickPixmap::Options options,
const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount,
1893 qreal devicePixelRatio)
1900 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1901 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1903 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1904 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1907 QQuickPixmap::Options orgOptions = options;
1910 options |= QQuickPixmap::Cache;
1916 if (url.scheme() == itemGrabberScheme) {
1919 if (requestSize != dummySize)
1920 qWarning() <<
"Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead.";
1921 const QQuickPixmapKey grabberKey = { &url, &dummyRegion, &dummySize, 0, QQuickImageProviderOptions() };
1922 iter = store->m_cache.find(grabberKey);
1923 }
else if (options & QQuickPixmap::Cache)
1924 iter = store->m_cache.find(key);
1926 if (iter == store->m_cache.end()) {
1932 if (url.scheme() == QLatin1String(
"image")) {
1933 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1934 if (
auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
1935 const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps);
1936 if (!threadedPixmaps && provider->imageType() == QQuickImageProvider::Pixmap) {
1938 options &= ~QQuickPixmap::Asynchronous;
1939 }
else if (provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading) {
1940 options |= QQuickPixmap::Asynchronous;
1945 if (!(options & QQuickPixmap::Asynchronous)) {
1947 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
1948 d = createPixmapDataSync(engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
1950 PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
1951 if (options & QQuickPixmap::Cache)
1954 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1959 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1964 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1965 QQuickImageProviderOptions::UsePluginDefaultTransform, frame,
1966 frameCount, devicePixelRatio);
1967 if (options & QQuickPixmap::Cache)
1970 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1973 QQuickPixmapReader::readerMutex.lock();
1974 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
1975 d->reply = reader->getImage(d);
1976 reader->startJob(d->reply);
1977 QQuickPixmapReader::readerMutex.unlock();
1981 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame;
1986
1987
1988
1989
1990
1991
1992void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device,
const QUrl &url,
1993 const QRect &requestRegion,
const QSize &requestSize,
1994 const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount)
1997 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1998 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1999 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
2000 QMutexLocker locker(&store->m_cacheMutex);
2001 iter = store->m_cache.find(key);
2002 if (iter == store->m_cache.end()) {
2007 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
2008 QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
2009 d->specialDevice = device;
2010 d->fromSpecialDevice =
true;
2013 QQuickPixmapReader::readerMutex.lock();
2014 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
2015 d->reply = reader->getImage(d);
2017 QObject::connect(d->reply, &QQuickPixmapReply::destroyed, store, [oldD]() {
2019 }, Qt::QueuedConnection);
2021 reader->startJob(d->reply);
2022 QQuickPixmapReader::readerMutex.unlock();
2026 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame <<
"refCount" << d->refCount;
2033void QQuickPixmap::clear()
2041void QQuickPixmap::clear(QObject *obj)
2045 QObject::disconnect(d->reply,
nullptr, obj,
nullptr);
2051bool QQuickPixmap::isCached(
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
2052 const int frame,
const QQuickImageProviderOptions &options)
2054 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, options };
2055 QQuickPixmapCache *store = QQuickPixmapCache::instance();
2057 return store->m_cache.contains(key);
2060bool QQuickPixmap::isScalableImageFormat(
const QUrl &url)
2062 if (url.scheme() ==
"image"_L1)
2065 const QString stringUrl = url.path(QUrl::PrettyDecoded);
2066 return stringUrl.endsWith(
"svg"_L1)
2067 || stringUrl.endsWith(
"svgz"_L1)
2068 || stringUrl.endsWith(
"pdf"_L1);
2071bool QQuickPixmap::connectFinished(QObject *object,
const char *method)
2073 if (!d || !d->reply) {
2074 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2078 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
2081bool QQuickPixmap::connectFinished(QObject *object,
int method)
2083 if (!d || !d->reply) {
2084 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2088 return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedMethodIndex, object, method);
2091bool QQuickPixmap::connectDownloadProgress(QObject *object,
const char *method)
2093 if (!d || !d->reply) {
2094 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2098 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object,
2102bool QQuickPixmap::connectDownloadProgress(QObject *object,
int method)
2104 if (!d || !d->reply) {
2105 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2109 return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressMethodIndex, object,
2113QColorSpace QQuickPixmap::colorSpace()
const
2115 if (!d || !d->textureFactory)
2116 return QColorSpace();
2117 return d->textureFactory->image().colorSpace();
2122#include <qquickpixmapcache.moc>
2124#include "moc_qquickpixmap_p.cpp"
2125#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