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
268
269
272 return ownedReaderThreadExecutionEnforcer.get();
274 std::unique_ptr<ReaderThreadExecutionEnforcer> ownedReaderThreadExecutionEnforcer;
277#if QT_CONFIG(qml_network)
282 QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses;
287 static int replyDownloadProgressMethodIndex;
288 static int replyFinishedMethodIndex;
289 static int downloadProgressMethodIndex;
290 static int threadNetworkRequestDoneMethodIndex;
291 static QHash<QQmlEngine *,QQuickPixmapReader*> readers;
296#if QT_CONFIG(quick_pixmap_cache_threaded_download)
297# define PIXMAP_READER_LOCK() QMutexLocker locker(&mutex)
299# define PIXMAP_READER_LOCK()
302class QQuickPixmapCache;
305
306
311 const QQuickImageProviderOptions &po,
const QString &e)
323 QQuickPixmapData(
const QUrl &u,
const QRect &r,
const QSize &s,
const QQuickImageProviderOptions &po,
324 QQuickImageProviderOptions::AutoTransform aTransform,
int frame=0,
int frameCount=1, qreal dpr = 1)
337 const QSize &s,
const QRect &r,
const QSize &rs,
const QQuickImageProviderOptions &po,
338 QQuickImageProviderOptions::AutoTransform aTransform,
int frame=0,
int frameCount=1, qreal dpr = 1)
360 requestSize = implicitSize = texture->textureSize();
365 delete textureFactory;
370 void release(QQuickPixmapCache *store =
nullptr);
426 const QSize &implicitSize, QQuickTextureFactory *factory)
429 QCoreApplication::postEvent(
this,
new Event(error, errorString, implicitSize, factory));
439 delete textureFactory;
442#if QT_CONFIG(qml_network)
443QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
445 if (!accessManager) {
446 Q_ASSERT(readerThreadExecutionEnforcer());
447 accessManager = QQmlTypeLoader::get(engine)->createNetworkAccessManager(
448 readerThreadExecutionEnforcer());
450 return accessManager;
457 if (image->hasAlphaChannel() && image->data_ptr()
458 && !image->data_ptr()->checkForAlphaPixels()) {
459 switch (image->format()) {
460 case QImage::Format_RGBA8888:
461 case QImage::Format_RGBA8888_Premultiplied:
462 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX8888, Qt::AutoColor))
465 *image = image->convertToFormat(QImage::Format_RGBX8888);
467 case QImage::Format_A2BGR30_Premultiplied:
468 if (image->data_ptr()->convertInPlace(QImage::Format_BGR30, Qt::AutoColor))
471 *image = image->convertToFormat(QImage::Format_BGR30);
473 case QImage::Format_A2RGB30_Premultiplied:
474 if (image->data_ptr()->convertInPlace(QImage::Format_RGB30, Qt::AutoColor))
477 *image = image->convertToFormat(QImage::Format_RGB30);
479 case QImage::Format_RGBA16FPx4:
480 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX16FPx4, Qt::AutoColor))
483 *image = image->convertToFormat(QImage::Format_RGBX16FPx4);
485 case QImage::Format_RGBA32FPx4:
486 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX32FPx4, Qt::AutoColor))
489 *image = image->convertToFormat(QImage::Format_RGBX32FPx4);
492 if (image->data_ptr()->convertInPlace(QImage::Format_RGB32, Qt::AutoColor))
495 *image = image->convertToFormat(QImage::Format_RGB32);
501static bool readImage(
const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
int *frameCount,
502 const QRect &requestRegion,
const QSize &requestSize,
const QQuickImageProviderOptions &providerOptions,
503 QQuickImageProviderOptions::AutoTransform *appliedTransform =
nullptr,
int frame = 0,
504 qreal devicePixelRatio = 1.0)
506 QImageReader imgio(dev);
507 if (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform)
508 imgio.setAutoTransform(providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform);
509 else if (appliedTransform)
510 *appliedTransform = imgio.autoTransform() ? QQuickImageProviderOptions::ApplyTransform : QQuickImageProviderOptions::DoNotApplyTransform;
512 if (frame < imgio.imageCount())
513 imgio.jumpToImage(frame);
516 *frameCount = imgio.imageCount();
518 QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions, devicePixelRatio);
519 if (scSize.isValid())
520 imgio.setScaledSize(scSize);
521 if (!requestRegion.isNull())
522 imgio.setScaledClipRect(requestRegion);
523 const QSize originalSize = imgio.size();
524 qCDebug(lcImg) << url <<
"frame" << frame <<
"of" << imgio.imageCount()
525 <<
"requestRegion" << requestRegion <<
"QImageReader size" << originalSize <<
"-> scSize" << scSize;
528 *impsize = originalSize;
530 if (imgio.read(image)) {
531 maybeRemoveAlpha(image);
532 if (impsize && impsize->width() < 0)
533 *impsize = image->size();
534 if (providerOptions.targetColorSpace().isValid()) {
535 if (image->colorSpace().isValid())
536 image->convertToColorSpace(providerOptions.targetColorSpace());
538 image->setColorSpace(providerOptions.targetColorSpace());
543 *errorString = QQuickPixmap::tr(
"Error decoding: %1: %2").arg(url.toString())
544 .arg(imgio.errorString());
552 res.reserve(list.size());
553 for (
const QByteArray &item : list)
554 res.append(QString::fromLatin1(item));
563 delete QSGContext::createTextureFactoryFromImage(QImage());
564 hasOpenGL = QQuickWindow::sceneGraphBackend().isEmpty();
565 QList<QByteArray> list;
567 list.append(QSGTextureReader::supportedFileFormats());
568 list.append(QImageReader::supportedImageFormats());
569 fileSuffixes = fromLatin1List(list);
581 QFileInfo fi(localFile);
582 if (!fi.suffix().isEmpty() || fi.exists())
585 QString tryFile = localFile + QStringLiteral(
".xxxx");
586 const int suffixIdx = localFile.size() + 1;
587 for (
const QString &suffix : backendSupport()->fileSuffixes) {
588 tryFile.replace(suffixIdx, 10, suffix);
589 if (QFileInfo::exists(tryFile))
596: QThread(eng), engine(eng)
597#if QT_CONFIG(qml_network)
598, accessManager(
nullptr)
604 engine->handle()->typeLoader();
607#if QT_CONFIG(quick_pixmap_cache_threaded_download)
608 start(QThread::LowestPriority);
619 readers.remove(engine);
620 readerMutex.unlock();
625 for (QQuickPixmapReply *reply : std::as_const(jobs)) {
626 if (reply->data && reply->data->reply == reply)
627 reply->data->reply =
nullptr;
633 cancelledJobs.append(reply);
637#if QT_CONFIG(qml_network)
638 for (
auto *reply : std::as_const(networkJobs))
641 for (
auto *reply : std::as_const(asyncResponses))
643#if !QT_CONFIG(quick_pixmap_cache_threaded_download)
649 if (readerThreadExecutionEnforcer())
654#if QT_CONFIG(quick_pixmap_cache_threaded_download)
667#if QT_CONFIG(qml_network)
668 for (QQuickPixmapReply *reply : std::as_const(networkJobs))
671 for (QQuickPixmapReply *reply : std::as_const(asyncResponses))
674#if QT_CONFIG(qml_network)
677 asyncResponses.clear();
680#if QT_CONFIG(qml_network)
681void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
683 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
685 QQuickPixmapReply *job = networkJobs.take(reply);
689 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
692 QQuickTextureFactory *factory =
nullptr;
693 if (reply->error()) {
694 error = QQuickPixmapReply::Loading;
695 errorString = reply->errorString();
697 QByteArray all = reply->readAll();
699 buff.open(QIODevice::ReadOnly);
700 QSGTextureReader texReader(&buff, reply->url().fileName());
701 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
702 factory = texReader.read();
704 readSize = factory->textureSize();
706 error = QQuickPixmapReply::Decoding;
707 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(reply->url().toString());
711 int const frame = job->data ? job->data->frame : 0;
712 const qreal dpr = job->data ? job->data->devicePixelRatio : 1;
713 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount,
714 job->requestRegion, job->requestSize, job->providerOptions,
nullptr, frame, dpr))
715 error = QQuickPixmapReply::Decoding;
717 job->data->frameCount = frameCount;
722 factory = QQuickTextureFactory::textureFactoryForImage(image);
724 PIXMAP_READER_LOCK();
725 if (!cancelledJobs.contains(job))
726 job->postReply(error, errorString, readSize, factory);
728 reply->deleteLater();
731 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
742 QQuickTextureFactory *t =
nullptr;
743 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
745 if (!response->errorString().isEmpty()) {
746 error = QQuickPixmapReply::Loading;
747 errorString = response->errorString();
749 t = response->textureFactory();
753 if (!cancelledJobs.contains(job))
754 job->postReply(error, errorString, t ? t->textureSize() : QSize(), t);
758 response->deleteLater();
768 QCoreApplication::postEvent(
769 this,
new QEvent(QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs)));
775 case QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs):
776 reader->processJobs();
779 return QObject::event(e);
785#if QT_CONFIG(qml_network)
786 QNetworkReply *reply =
static_cast<QNetworkReply *>(sender());
787 reader->networkRequestDone(reply);
793 reader->asyncResponseFinished(response);
798 QQuickImageResponse *response =
static_cast<QQuickImageResponse *>(sender());
808 if (cancelledJobs.isEmpty() && jobs.isEmpty())
812 if (!cancelledJobs.isEmpty()) {
813 for (
int i = 0; i < cancelledJobs.size(); ++i) {
815#if QT_CONFIG(qml_network)
816 QNetworkReply *reply = networkJobs.key(job, 0);
818 networkJobs.remove(reply);
819 if (reply->isRunning()) {
826 QQuickImageResponse *asyncResponse = asyncResponses.key(job);
828 asyncResponses.remove(asyncResponse);
829 asyncResponse->cancel();
832 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url));
836 cancelledJobs.clear();
839 if (!jobs.isEmpty()) {
841 bool usableJob =
false;
842 for (
int i = jobs.size() - 1; !usableJob && i >= 0; i--) {
844 const QUrl url = job->url;
846 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
847 QSharedPointer<QQuickImageProvider> provider;
849 if (url.scheme() == QLatin1String(
"image")) {
850 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
851 provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>();
853 imageType = provider->imageType();
857 localFile = QQmlFile::urlToLocalFileOrQrc(url);
860 if (localFile.isEmpty() && url.scheme() == QLatin1String(
"content"))
861 localFile = url.toString();
862 usableJob = !localFile.isEmpty()
863#if QT_CONFIG(qml_network)
864 || networkJobs.size() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
874 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
876#if QT_CONFIG(quick_pixmap_cache_threaded_download)
878 auto relockMutexGuard = qScopeGuard(([&locker]() {
882 processJob(job, url, localFile, imageType, provider);
893 QQuickImageProvider::ImageType imageType,
const QSharedPointer<QQuickImageProvider> &provider)
898 if (url.scheme() == QLatin1String(
"image")) {
902 if (imageType == QQuickImageProvider::Invalid) {
903 QString errorStr = QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString());
905 if (!cancelledJobs.contains(runningJob))
906 runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize,
nullptr);
911 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
914 case QQuickImageProvider::Invalid:
920 case QQuickImageProvider::Image:
924 image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
926 image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize);
928 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
930 if (image.isNull()) {
931 errorCode = QQuickPixmapReply::Loading;
932 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
935 if (!cancelledJobs.contains(runningJob)) {
936 runningJob->postReply(errorCode, errorStr, readSize,
937 QQuickTextureFactory::textureFactoryForImage(image));
942 case QQuickImageProvider::Pixmap:
946 pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
948 pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize);
950 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
952 if (pixmap.isNull()) {
953 errorCode = QQuickPixmapReply::Loading;
954 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
958 if (!cancelledJobs.contains(runningJob)) {
959 runningJob->postReply(
960 errorCode, errorStr, readSize,
961 QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()));
966 case QQuickImageProvider::Texture:
968 QQuickTextureFactory *t;
970 t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
972 t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize);
974 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
977 errorCode = QQuickPixmapReply::Loading;
978 errorStr = QQuickPixmap::tr(
"Failed to get texture from provider: %1").arg(url.toString());
981 if (!cancelledJobs.contains(runningJob))
982 runningJob->postReply(errorCode, errorStr, readSize, t);
988 case QQuickImageProvider::ImageResponse:
990 QQuickImageResponse *response;
992 response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions);
994 QQuickAsyncImageProvider *asyncProvider =
static_cast<QQuickAsyncImageProvider*>(provider.get());
995 response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
999 QObject::connect(response, &QQuickImageResponse::finished, readerThreadExecutionEnforcer(),
1000 qOverload<>(&ReaderThreadExecutionEnforcer::asyncResponseFinished));
1003 auto provider_copy = provider;
1004 QObject::connect(response, &QQuickImageResponse::destroyed, response, [provider_copy]() {
1012 if (
static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
1013 QMetaObject::invokeMethod(readerThreadExecutionEnforcer(),
"asyncResponseFinished",
1014 Qt::QueuedConnection,
1015 Q_ARG(QQuickImageResponse *, response));
1018 asyncResponses.insert(response, runningJob);
1024 if (!localFile.isEmpty()) {
1027 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
1032 auto specialDevice = runningJob
->data->specialDevice;
1033 if (specialDevice.isNull() || QObjectPrivate::get(specialDevice.data())->deleteLaterCalled) {
1034 qCDebug(lcImg) <<
"readImage job aborted" << url;
1041 if (!specialDevice->thread()) {
1042 qCDebug(lcQsgLeak) << specialDevice.data() <<
": changing thread affinity so that"
1043 << QThread::currentThread() <<
"will handle any deleteLater() calls";
1044 specialDevice->moveToThread(QThread::currentThread());
1046 if (!readImage(url, specialDevice.data(), &image, &errorStr, &readSize, &frameCount,
1047 runningJob->requestRegion, runningJob->requestSize,
1048 runningJob->providerOptions,
nullptr, runningJob->data->frame,
1049 runningJob->data->devicePixelRatio)) {
1050 errorCode = QQuickPixmapReply::Loading;
1051 }
else if (runningJob
->data) {
1055 QFile f(existingImageFileForPath(localFile));
1056 if (f.open(QIODevice::ReadOnly)) {
1057 QSGTextureReader texReader(&f, localFile);
1058 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1059 QQuickTextureFactory *factory = texReader.read();
1061 readSize = factory->textureSize();
1063 errorStr = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1064 if (f.fileName() != localFile)
1065 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1066 errorCode = QQuickPixmapReply::Decoding;
1069 if (!cancelledJobs.contains(runningJob))
1070 runningJob->postReply(errorCode, errorStr, readSize, factory);
1075 const qreal dpr = runningJob
->data ? runningJob
->data->devicePixelRatio : 1;
1076 if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
1077 runningJob->requestRegion, runningJob->requestSize,
1078 runningJob->providerOptions,
nullptr, frame, dpr)) {
1079 errorCode = QQuickPixmapReply::Loading;
1080 if (f.fileName() != localFile)
1081 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1082 }
else if (runningJob
->data) {
1087 errorStr = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1088 errorCode = QQuickPixmapReply::Loading;
1092 if (!cancelledJobs.contains(runningJob)) {
1093 runningJob->postReply(errorCode, errorStr, readSize,
1094 QQuickTextureFactory::textureFactoryForImage(image));
1097#if QT_CONFIG(qml_network)
1099 QNetworkRequest req(url);
1100 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
1101 QNetworkReply *reply = networkAccessManager()->get(req);
1103 QMetaObject::connect(reply, replyDownloadProgressMethodIndex, runningJob,
1104 downloadProgressMethodIndex);
1105 QMetaObject::connect(reply, replyFinishedMethodIndex, readerThreadExecutionEnforcer(),
1106 threadNetworkRequestDoneMethodIndex);
1108 networkJobs.insert(reply, runningJob);
1122 readers.insert(engine, reader);
1131 return readers.value(engine, 0);
1137 reply->engineForReader = engine;
1145 if (readerThreadExecutionEnforcer())
1153 cancelledJobs.append(reply);
1156 if (readerThreadExecutionEnforcer())
1161 if (jobs.removeAll(reply) == 0) {
1162 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(reply->url));
1172 if (replyDownloadProgressMethodIndex == -1) {
1173#if QT_CONFIG(qml_network)
1174 replyDownloadProgressMethodIndex =
1175 QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
1176 replyFinishedMethodIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
1177 const QMetaObject *ir = &ReaderThreadExecutionEnforcer::staticMetaObject;
1178 threadNetworkRequestDoneMethodIndex = ir->indexOfSlot(
"networkRequestDone()");
1180 downloadProgressMethodIndex =
1181 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1184#if QT_CONFIG(quick_pixmap_cache_threaded_download)
1185 const auto guard = qScopeGuard([
this]() {
1187 PIXMAP_READER_LOCK();
1188 delete runLoopReaderThreadExecutionEnforcer;
1189 runLoopReaderThreadExecutionEnforcer =
nullptr;
1193 PIXMAP_READER_LOCK();
1194 Q_ASSERT(!runLoopReaderThreadExecutionEnforcer);
1195 runLoopReaderThreadExecutionEnforcer =
new ReaderThreadExecutionEnforcer(
this);
1201 ownedReaderThreadExecutionEnforcer = std::make_unique<ReaderThreadExecutionEnforcer>(
this);
1206inline bool operator==(
const QQuickPixmapKey &lhs,
const QQuickPixmapKey &rhs)
1208 return *lhs.url == *rhs.url &&
1209 *lhs.region == *rhs.region &&
1210 *lhs.size == *rhs.size &&
1211 lhs.frame == rhs.frame &&
1212 lhs.options == rhs.options;
1217 return qHashMulti(seed, *key.url, *key.region, *key.size, key.frame, key.options.autoTransform());
1220#ifndef QT_NO_DEBUG_STREAM
1223 QDebugStateSaver saver(debug);
1226 debug <<
"QQuickPixmapKey(0)";
1230 debug <<
"QQuickPixmapKey(" << key.url->toString() <<
" frame=" << key.frame;
1231 if (!key.region->isEmpty()) {
1232 debug <<
" region=";
1233 QtDebugUtils::formatQRect(debug, *key.region);
1235 if (!key.size->isEmpty()) {
1237 QtDebugUtils::formatQSize(debug, *key.size);
1244QQuickPixmapCache *QQuickPixmapCache::instance()
1246 static QQuickPixmapCache self;
1250QQuickPixmapCache::~QQuickPixmapCache()
1256
1257
1258
1259
1260
1261
1262
1263int QQuickPixmapCache::destroyCache()
1268 m_destroying =
true;
1276 int leakedPixmaps = 0;
1277 const auto cache = m_cache;
1278 for (
auto *pixmap : cache) {
1279 auto currRefCount = pixmap->refCount;
1282 qCDebug(lcQsgLeak) <<
"leaked pixmap: refCount" << pixmap->refCount << pixmap->url <<
"frame" << pixmap->frame
1283 <<
"size" << pixmap->requestSize <<
"region" << pixmap->requestRegion;
1284 while (currRefCount > 0) {
1285 pixmap->release(
this);
1292 while (m_lastUnreferencedPixmap)
1295 qCDebug(lcQsgLeak,
"Number of leaked pixmaps: %i", leakedPixmaps);
1296 return leakedPixmaps;
1299qsizetype QQuickPixmapCache::referencedCost()
const
1302 QMutexLocker locker(&m_cacheMutex);
1303 for (
const auto *pixmap : std::as_const(m_cache)) {
1304 if (pixmap->refCount)
1305 ret += pixmap->cost();
1311
1312
1313
1314void QQuickPixmapCache::unreferencePixmap(QQuickPixmapData *data)
1316 Q_ASSERT(data->prevUnreferenced ==
nullptr);
1317 Q_ASSERT(data->prevUnreferencedPtr ==
nullptr);
1318 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1320 data->nextUnreferenced = m_unreferencedPixmaps;
1321 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
1322 if (!m_destroying) {
1323 m_unreferencedCost += data->cost();
1324 qCDebug(lcImg) << data->url <<
"had cost" << data->cost() <<
"of total unreferenced" << m_unreferencedCost;
1327 m_unreferencedPixmaps = data;
1328 if (m_unreferencedPixmaps->nextUnreferenced) {
1329 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
1330 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
1333 if (!m_lastUnreferencedPixmap)
1334 m_lastUnreferencedPixmap = data;
1338 if (m_timerId == -1 && m_unreferencedPixmaps
1339 && !m_destroying && !QCoreApplication::closingDown()) {
1345
1346
1347
1348void QQuickPixmapCache::referencePixmap(QQuickPixmapData *data)
1350 Q_ASSERT(data->prevUnreferencedPtr);
1352 *data->prevUnreferencedPtr = data->nextUnreferenced;
1353 if (data->nextUnreferenced) {
1354 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
1355 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
1357 if (m_lastUnreferencedPixmap == data)
1358 m_lastUnreferencedPixmap = data->prevUnreferenced;
1360 data->nextUnreferenced =
nullptr;
1361 data->prevUnreferencedPtr =
nullptr;
1362 data->prevUnreferenced =
nullptr;
1364 m_unreferencedCost -= data->cost();
1365 qCDebug(lcImg) << data->url <<
"subtracts cost" << data->cost() <<
"of total" << m_unreferencedCost;
1369
1370
1371
1372void QQuickPixmapCache::shrinkCache(
int remove)
1374 qCDebug(lcImg) <<
"reduce unreferenced cost" << m_unreferencedCost <<
"to less than limit" << cache_limit;
1375 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
1376 QQuickPixmapData *data = m_lastUnreferencedPixmap;
1377 Q_ASSERT(data->nextUnreferenced ==
nullptr);
1379 *data->prevUnreferencedPtr =
nullptr;
1380 m_lastUnreferencedPixmap = data->prevUnreferenced;
1381 data->prevUnreferencedPtr =
nullptr;
1382 data->prevUnreferenced =
nullptr;
1384 if (!m_destroying) {
1385 remove -= data->cost();
1386 m_unreferencedCost -= data->cost();
1388 data->removeFromCache(
this);
1393void QQuickPixmapCache::timerEvent(QTimerEvent *)
1397 shrinkCache(removalCost);
1399 if (m_unreferencedPixmaps ==
nullptr) {
1400 killTimer(m_timerId);
1405void QQuickPixmapCache::purgeCache()
1407 shrinkCache(m_unreferencedCost);
1410void QQuickPixmap::purgeCache()
1412 QQuickPixmapCache::instance()->purgeCache();
1419 if (finishedMethodIndex == -1) {
1420 finishedMethodIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex();
1421 downloadProgressMethodIndex =
1422 QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
1434 if (event->type() == QEvent::User) {
1438 data->pixmapStatus = (de->error == NoError) ? QQuickPixmap::Ready : QQuickPixmap::Error;
1439 if (data->pixmapStatus == QQuickPixmap::Ready) {
1440 data->textureFactory = de->textureFactory;
1441 de->textureFactory =
nullptr;
1442 data->implicitSize = de->implicitSize;
1444 data->textureFactory !=
nullptr &&
data->textureFactory->textureSize().isValid() ?
1445 data->textureFactory->textureSize() :
1446 (
data->requestSize.isValid() ?
data->requestSize :
data->implicitSize)));
1448 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(data->url));
1449 data->errorString = de->errorString;
1456 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1462 return QObject::event(event);
1469 return textureFactory->textureByteCount();
1476 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1477 if (prevUnreferencedPtr)
1478 QQuickPixmapCache::instance()->referencePixmap(
this);
1483 Q_ASSERT(refCount > 0);
1485 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1486 if (refCount == 0) {
1491 QQuickPixmapReader::readerMutex.lock();
1492 QQuickPixmapReader *reader = QQuickPixmapReader::existingInstance(cancelReply->engineForReader);
1495 QQuickPixmapReader::readerMutex.unlock();
1498 store = store ? store : QQuickPixmapCache::instance();
1499 if (pixmapStatus == QQuickPixmap::Ready
1505 store->unreferencePixmap(
this);
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1529 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1530 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1531 if (lcImg().isDebugEnabled()) {
1532 qCDebug(lcImg) <<
"adding" << key <<
"to total" << QQuickPixmapCache::instance()->m_cache.size();
1533 for (
auto it = QQuickPixmapCache::instance()->m_cache.keyBegin(); it != QQuickPixmapCache::instance()->m_cache.keyEnd(); ++it) {
1534 if (*(it->url) == url && it->frame == frame)
1535 qCDebug(lcImg) <<
" similar pre-existing:" << *it;
1538 QQuickPixmapCache::instance()->m_cache.insert(key,
this);
1540 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1541 url, QQuickPixmapCache::instance()->m_cache.size()));
1549 store = QQuickPixmapCache::instance();
1550 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1551 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1552 store->m_cache.remove(key);
1553 qCDebug(lcImg) <<
"removed" << key << implicitSize <<
"; total remaining" << QQuickPixmapCache::instance()->m_cache.size();
1555 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1556 url, store->m_cache.size()));
1561 const QRect &requestRegion,
const QSize &requestSize,
1562 const QQuickImageProviderOptions &providerOptions,
int frame,
bool *ok,
1563 qreal devicePixelRatio)
1565 if (url.scheme() == QLatin1String(
"image")) {
1568 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
1569 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1570 QSharedPointer<QQuickImageProvider> provider = enginePrivate->imageProvider(imageProviderId(url)).objectCast<QQuickImageProvider>();
1572 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
1574 imageType = provider->imageType();
1576 switch (imageType) {
1577 case QQuickImageProvider::Invalid:
1578 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1579 QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString()));
1580 case QQuickImageProvider::Texture:
1582 QQuickTextureFactory *texture = providerV2 ? providerV2->requestTexture(imageId(url), &readSize, requestSize, providerOptions)
1583 : provider->requestTexture(imageId(url), &readSize, requestSize);
1586 return new QQuickPixmapData(url, texture, readSize, requestRegion, requestSize,
1587 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1592 case QQuickImageProvider::Image:
1594 QImage image = providerV2 ? providerV2->requestImage(imageId(url), &readSize, requestSize, providerOptions)
1595 : provider->requestImage(imageId(url), &readSize, requestSize);
1596 if (!image.isNull()) {
1598 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image),
1599 readSize, requestRegion, requestSize, providerOptions,
1600 QQuickImageProviderOptions::UsePluginDefaultTransform,
1601 frame, 1, devicePixelRatio);
1605 case QQuickImageProvider::Pixmap:
1607 QPixmap pixmap = providerV2 ? providerV2->requestPixmap(imageId(url), &readSize, requestSize, providerOptions)
1608 : provider->requestPixmap(imageId(url), &readSize, requestSize);
1609 if (!pixmap.isNull()) {
1611 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()),
1612 readSize, requestRegion, requestSize, providerOptions,
1613 QQuickImageProviderOptions::UsePluginDefaultTransform,
1614 frame, 1, devicePixelRatio);
1618 case QQuickImageProvider::ImageResponse:
1621 Q_ASSERT(imageType != QQuickImageProvider::ImageResponse &&
"Sync call to ImageResponse provider");
1626 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1627 QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString()));
1630 QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
1631 if (localFile.isEmpty())
1634 QFile f(existingImageFileForPath(localFile));
1636 QString errorString;
1638 if (f.open(QIODevice::ReadOnly)) {
1639 QSGTextureReader texReader(&f, localFile);
1640 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1641 QQuickTextureFactory *factory = texReader.read();
1644 return new QQuickPixmapData(url, factory, factory->textureSize(), requestRegion, requestSize,
1645 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1647 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1648 if (f.fileName() != localFile)
1649 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1653 QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
1655 if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
1656 providerOptions, &appliedTransform, frame, devicePixelRatio)) {
1658 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
1659 providerOptions, appliedTransform, frame, frameCount);
1660 }
else if (f.fileName() != localFile) {
1661 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1665 errorString = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1667 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, errorString);
1678QQuickPixmap::QQuickPixmap()
1683QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url)
1689QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url, Options options)
1692 load(engine, url, options);
1695QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url,
const QRect ®ion,
const QSize &size)
1698 load(engine, url, region, size);
1701QQuickPixmap::QQuickPixmap(
const QUrl &url,
const QImage &image)
1703 d =
new QQuickPixmapData(url,
new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
1704 QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform,
1705 0, 1, image.devicePixelRatio());
1709QQuickPixmap::~QQuickPixmap()
1717bool QQuickPixmap::isNull()
const
1719 return d ==
nullptr;
1722bool QQuickPixmap::isReady()
const
1724 return status() == Ready;
1727bool QQuickPixmap::isError()
const
1729 return status() == Error;
1732bool QQuickPixmap::isLoading()
const
1734 return status() == Loading;
1737QString QQuickPixmap::error()
const
1740 return d->errorString;
1745QQuickPixmap::Status QQuickPixmap::status()
const
1748 return d->pixmapStatus;
1753const QUrl &QQuickPixmap::url()
const
1758 return nullPixmap()->url;
1761const QSize &QQuickPixmap::implicitSize()
const
1764 return d->implicitSize;
1766 return nullPixmap()->size;
1769const QSize &QQuickPixmap::requestSize()
const
1772 return d->requestSize;
1774 return nullPixmap()->size;
1777const QRect &QQuickPixmap::requestRegion()
const
1780 return d->requestRegion;
1782 return nullPixmap()->region;
1785QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform()
const
1788 return d->appliedTransform;
1790 return QQuickImageProviderOptions::UsePluginDefaultTransform;
1793int QQuickPixmap::frameCount()
const
1796 return d->frameCount;
1800QQuickTextureFactory *QQuickPixmap::textureFactory()
const
1803 return d->textureFactory;
1808QImage QQuickPixmap::image()
const
1810 if (d && d->textureFactory)
1811 return d->textureFactory->image();
1815void QQuickPixmap::setImage(
const QImage &p)
1822 d =
new QQuickPixmapData(QQuickTextureFactory::textureFactoryForImage(p));
1826void QQuickPixmap::setPixmap(
const QQuickPixmap &other)
1840int QQuickPixmap::width()
const
1842 if (d && d->textureFactory)
1843 return d->textureFactory->textureSize().width();
1848int QQuickPixmap::height()
const
1850 if (d && d->textureFactory)
1851 return d->textureFactory->textureSize().height();
1856qreal QQuickPixmap::devicePixelRatio()
const
1858 return d ? d->devicePixelRatio : 1.0;
1861QRect QQuickPixmap::rect()
const
1863 if (d && d->textureFactory)
1864 return QRect(QPoint(), d->textureFactory->textureSize());
1869void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url)
1871 load(engine, url, QRect(), QSize(), QQuickPixmap::Cache);
1874void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url, QQuickPixmap::Options options)
1876 load(engine, url, QRect(), QSize(), options);
1879void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize)
1881 load(engine, url, requestRegion, requestSize, QQuickPixmap::Cache);
1884void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize, QQuickPixmap::Options options)
1886 load(engine, url, requestRegion, requestSize, options, QQuickImageProviderOptions());
1889void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
1890 QQuickPixmap::Options options,
const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount,
1891 qreal devicePixelRatio)
1898 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1899 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1901 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1902 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1905 QQuickPixmap::Options orgOptions = options;
1908 options |= QQuickPixmap::Cache;
1914 if (url.scheme() == itemGrabberScheme) {
1917 if (requestSize != dummySize)
1918 qWarning() <<
"Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead.";
1919 const QQuickPixmapKey grabberKey = { &url, &dummyRegion, &dummySize, 0, QQuickImageProviderOptions() };
1920 iter = store->m_cache.find(grabberKey);
1921 }
else if (options & QQuickPixmap::Cache)
1922 iter = store->m_cache.find(key);
1924 if (iter == store->m_cache.end()) {
1930 if (url.scheme() == QLatin1String(
"image")) {
1931 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1932 if (
auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
1933 const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps);
1934 if (!threadedPixmaps && provider->imageType() == QQuickImageProvider::Pixmap) {
1936 options &= ~QQuickPixmap::Asynchronous;
1937 }
else if (provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading) {
1938 options |= QQuickPixmap::Asynchronous;
1943 if (!(options & QQuickPixmap::Asynchronous)) {
1945 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
1946 d = createPixmapDataSync(engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
1948 PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
1949 if (options & QQuickPixmap::Cache)
1952 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1957 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1962 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1963 QQuickImageProviderOptions::UsePluginDefaultTransform, frame,
1964 frameCount, devicePixelRatio);
1965 if (options & QQuickPixmap::Cache)
1968 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1971 QQuickPixmapReader::readerMutex.lock();
1972 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
1973 d->reply = reader->getImage(d);
1974 reader->startJob(d->reply);
1975 QQuickPixmapReader::readerMutex.unlock();
1979 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame;
1984
1985
1986
1987
1988
1989
1990void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device,
const QUrl &url,
1991 const QRect &requestRegion,
const QSize &requestSize,
1992 const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount)
1995 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1996 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1997 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1998 QMutexLocker locker(&store->m_cacheMutex);
1999 iter = store->m_cache.find(key);
2000 if (iter == store->m_cache.end()) {
2005 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
2006 QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
2007 d->specialDevice = device;
2008 d->fromSpecialDevice =
true;
2011 QQuickPixmapReader::readerMutex.lock();
2012 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
2013 d->reply = reader->getImage(d);
2015 QObject::connect(d->reply, &QQuickPixmapReply::destroyed, store, [oldD]() {
2017 }, Qt::QueuedConnection);
2019 reader->startJob(d->reply);
2020 QQuickPixmapReader::readerMutex.unlock();
2024 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame <<
"refCount" << d->refCount;
2031void QQuickPixmap::clear()
2039void QQuickPixmap::clear(QObject *obj)
2043 QObject::disconnect(d->reply,
nullptr, obj,
nullptr);
2049bool QQuickPixmap::isCached(
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
2050 const int frame,
const QQuickImageProviderOptions &options)
2052 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, options };
2053 QQuickPixmapCache *store = QQuickPixmapCache::instance();
2055 return store->m_cache.contains(key);
2058bool QQuickPixmap::isScalableImageFormat(
const QUrl &url)
2060 if (url.scheme() ==
"image"_L1)
2063 const QString stringUrl = url.path(QUrl::PrettyDecoded);
2064 return stringUrl.endsWith(
"svg"_L1)
2065 || stringUrl.endsWith(
"svgz"_L1)
2066 || stringUrl.endsWith(
"pdf"_L1);
2069bool QQuickPixmap::connectFinished(QObject *object,
const char *method)
2071 if (!d || !d->reply) {
2072 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2076 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
2079bool QQuickPixmap::connectFinished(QObject *object,
int method)
2081 if (!d || !d->reply) {
2082 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2086 return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedMethodIndex, object, method);
2089bool QQuickPixmap::connectDownloadProgress(QObject *object,
const char *method)
2091 if (!d || !d->reply) {
2092 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2096 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object,
2100bool QQuickPixmap::connectDownloadProgress(QObject *object,
int method)
2102 if (!d || !d->reply) {
2103 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2107 return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressMethodIndex, object,
2111QColorSpace QQuickPixmap::colorSpace()
const
2113 if (!d || !d->textureFactory)
2114 return QColorSpace();
2115 return d->textureFactory->image().colorSpace();
2120#include <qquickpixmapcache.moc>
2122#include "moc_qquickpixmap_p.cpp"
2123#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