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)
338 const QSize &s,
const QRect &r,
const QSize &rs,
const QQuickImageProviderOptions &po,
339 QQuickImageProviderOptions::AutoTransform aTransform,
int frame=0,
int frameCount=1)
361 requestSize = implicitSize = texture->textureSize();
366 delete textureFactory;
371 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 eventLoopQuitHack =
new QObject;
609 eventLoopQuitHack->moveToThread(
this);
610 QObject::connect(eventLoopQuitHack, &QObject::destroyed,
this, &QThread::quit, Qt::DirectConnection);
611 start(QThread::LowestPriority);
622 readers.remove(engine);
623 readerMutex.unlock();
628 for (QQuickPixmapReply *reply : std::as_const(jobs)) {
629 if (reply->data && reply->data->reply == reply)
630 reply->data->reply =
nullptr;
636 cancelledJobs.append(reply);
640#if QT_CONFIG(qml_network)
641 for (
auto *reply : std::as_const(networkJobs))
644 for (
auto *reply : std::as_const(asyncResponses))
646#if !QT_CONFIG(quick_pixmap_cache_threaded_download)
652 if (readerThreadExecutionEnforcer())
657#if QT_CONFIG(quick_pixmap_cache_threaded_download)
660 eventLoopQuitHack->deleteLater();
674#if QT_CONFIG(qml_network)
675 for (QQuickPixmapReply *reply : std::as_const(networkJobs))
678 for (QQuickPixmapReply *reply : std::as_const(asyncResponses))
681#if QT_CONFIG(qml_network)
684 asyncResponses.clear();
687#if QT_CONFIG(qml_network)
688void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
690 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
692 QQuickPixmapReply *job = networkJobs.take(reply);
696 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
699 QQuickTextureFactory *factory =
nullptr;
700 if (reply->error()) {
701 error = QQuickPixmapReply::Loading;
702 errorString = reply->errorString();
704 QByteArray all = reply->readAll();
706 buff.open(QIODevice::ReadOnly);
707 QSGTextureReader texReader(&buff, reply->url().fileName());
708 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
709 factory = texReader.read();
711 readSize = factory->textureSize();
713 error = QQuickPixmapReply::Decoding;
714 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(reply->url().toString());
718 int const frame = job->data ? job->data->frame : 0;
719 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount,
720 job->requestRegion, job->requestSize, job->providerOptions,
nullptr, frame))
721 error = QQuickPixmapReply::Decoding;
723 job->data->frameCount = frameCount;
728 factory = QQuickTextureFactory::textureFactoryForImage(image);
730 PIXMAP_READER_LOCK();
731 if (!cancelledJobs.contains(job))
732 job->postReply(error, errorString, readSize, factory);
734 reply->deleteLater();
737 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
748 QQuickTextureFactory *t =
nullptr;
749 QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError;
751 if (!response->errorString().isEmpty()) {
752 error = QQuickPixmapReply::Loading;
753 errorString = response->errorString();
755 t = response->textureFactory();
759 if (!cancelledJobs.contains(job))
760 job->postReply(error, errorString, t ? t->textureSize() : QSize(), t);
764 response->deleteLater();
774 QCoreApplication::postEvent(
775 this,
new QEvent(QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs)));
781 case QEvent::Type(ReaderThreadExecutionEnforcer::ProcessJobs):
782 reader->processJobs();
785 return QObject::event(e);
791#if QT_CONFIG(qml_network)
792 QNetworkReply *reply =
static_cast<QNetworkReply *>(sender());
793 reader->networkRequestDone(reply);
799 reader->asyncResponseFinished(response);
804 QQuickImageResponse *response =
static_cast<QQuickImageResponse *>(sender());
814 if (cancelledJobs.isEmpty() && jobs.isEmpty())
818 if (!cancelledJobs.isEmpty()) {
819 for (
int i = 0; i < cancelledJobs.size(); ++i) {
821#if QT_CONFIG(qml_network)
822 QNetworkReply *reply = networkJobs.key(job, 0);
824 networkJobs.remove(reply);
825 if (reply->isRunning()) {
832 QQuickImageResponse *asyncResponse = asyncResponses.key(job);
834 asyncResponses.remove(asyncResponse);
835 asyncResponse->cancel();
838 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url));
842 cancelledJobs.clear();
845 if (!jobs.isEmpty()) {
847 bool usableJob =
false;
848 for (
int i = jobs.size() - 1; !usableJob && i >= 0; i--) {
850 const QUrl url = job->url;
852 QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
853 QSharedPointer<QQuickImageProvider> provider;
855 if (url.scheme() == QLatin1String(
"image")) {
856 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
857 provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>();
859 imageType = provider->imageType();
863 localFile = QQmlFile::urlToLocalFileOrQrc(url);
864 usableJob = !localFile.isEmpty()
865#if QT_CONFIG(qml_network)
866 || networkJobs.size() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
876 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
878#if QT_CONFIG(quick_pixmap_cache_threaded_download)
880 auto relockMutexGuard = qScopeGuard(([&locker]() {
884 processJob(job, url, localFile, imageType, provider);
895 QQuickImageProvider::ImageType imageType,
const QSharedPointer<QQuickImageProvider> &provider)
900 if (url.scheme() == QLatin1String(
"image")) {
904 if (imageType == QQuickImageProvider::Invalid) {
905 QString errorStr = QQuickPixmap::tr(
"Invalid image provider: %1").arg(url.toString());
907 if (!cancelledJobs.contains(runningJob))
908 runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize,
nullptr);
913 QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
916 case QQuickImageProvider::Invalid:
922 case QQuickImageProvider::Image:
926 image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
928 image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize);
930 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
932 if (image.isNull()) {
933 errorCode = QQuickPixmapReply::Loading;
934 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
937 if (!cancelledJobs.contains(runningJob)) {
938 runningJob->postReply(errorCode, errorStr, readSize,
939 QQuickTextureFactory::textureFactoryForImage(image));
944 case QQuickImageProvider::Pixmap:
948 pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
950 pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize);
952 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
954 if (pixmap.isNull()) {
955 errorCode = QQuickPixmapReply::Loading;
956 errorStr = QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString());
960 if (!cancelledJobs.contains(runningJob)) {
961 runningJob->postReply(
962 errorCode, errorStr, readSize,
963 QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()));
968 case QQuickImageProvider::Texture:
970 QQuickTextureFactory *t;
972 t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
974 t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize);
976 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
979 errorCode = QQuickPixmapReply::Loading;
980 errorStr = QQuickPixmap::tr(
"Failed to get texture from provider: %1").arg(url.toString());
983 if (!cancelledJobs.contains(runningJob))
984 runningJob->postReply(errorCode, errorStr, readSize, t);
990 case QQuickImageProvider::ImageResponse:
992 QQuickImageResponse *response;
994 response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions);
996 QQuickAsyncImageProvider *asyncProvider =
static_cast<QQuickAsyncImageProvider*>(provider.get());
997 response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
1001 QObject::connect(response, &QQuickImageResponse::finished, readerThreadExecutionEnforcer(),
1002 qOverload<>(&ReaderThreadExecutionEnforcer::asyncResponseFinished));
1005 auto provider_copy = provider;
1006 QObject::connect(response, &QQuickImageResponse::destroyed, response, [provider_copy]() {
1014 if (
static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
1015 QMetaObject::invokeMethod(readerThreadExecutionEnforcer(),
"asyncResponseFinished",
1016 Qt::QueuedConnection,
1017 Q_ARG(QQuickImageResponse *, response));
1020 asyncResponses.insert(response, runningJob);
1026 if (!localFile.isEmpty()) {
1029 QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError;
1034 auto specialDevice = runningJob
->data->specialDevice;
1035 if (specialDevice.isNull() || QObjectPrivate::get(specialDevice.data())->deleteLaterCalled) {
1036 qCDebug(lcImg) <<
"readImage job aborted" << url;
1043 if (!specialDevice->thread()) {
1044 qCDebug(lcQsgLeak) << specialDevice.data() <<
": changing thread affinity so that"
1045 << QThread::currentThread() <<
"will handle any deleteLater() calls";
1046 specialDevice->moveToThread(QThread::currentThread());
1048 if (!readImage(url, specialDevice.data(), &image, &errorStr, &readSize, &frameCount,
1049 runningJob->requestRegion, runningJob->requestSize,
1050 runningJob->providerOptions,
nullptr, runningJob->data->frame)) {
1051 errorCode = QQuickPixmapReply::Loading;
1052 }
else if (runningJob
->data) {
1056 QFile f(existingImageFileForPath(localFile));
1057 if (f.open(QIODevice::ReadOnly)) {
1058 QSGTextureReader texReader(&f, localFile);
1059 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1060 QQuickTextureFactory *factory = texReader.read();
1062 readSize = factory->textureSize();
1064 errorStr = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1065 if (f.fileName() != localFile)
1066 errorStr += QString::fromLatin1(
" (%1)").arg(f.fileName());
1067 errorCode = QQuickPixmapReply::Decoding;
1070 if (!cancelledJobs.contains(runningJob))
1071 runningJob->postReply(errorCode, errorStr, readSize, factory);
1076 if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
1077 runningJob->requestRegion, runningJob->requestSize,
1078 runningJob->providerOptions,
nullptr, frame)) {
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, frame);
1604 case QQuickImageProvider::Pixmap:
1606 QPixmap pixmap = providerV2 ? providerV2->requestPixmap(imageId(url), &readSize, requestSize, providerOptions)
1607 : provider->requestPixmap(imageId(url), &readSize, requestSize);
1608 if (!pixmap.isNull()) {
1610 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()),
1611 readSize, requestRegion, requestSize, providerOptions,
1612 QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1616 case QQuickImageProvider::ImageResponse:
1619 Q_ASSERT(imageType != QQuickImageProvider::ImageResponse &&
"Sync call to ImageResponse provider");
1624 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1625 QQuickPixmap::tr(
"Failed to get image from provider: %1").arg(url.toString()));
1628 QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
1629 if (localFile.isEmpty())
1632 QFile f(existingImageFileForPath(localFile));
1634 QString errorString;
1636 if (f.open(QIODevice::ReadOnly)) {
1637 QSGTextureReader texReader(&f, localFile);
1638 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1639 QQuickTextureFactory *factory = texReader.read();
1642 return new QQuickPixmapData(url, factory, factory->textureSize(), requestRegion, requestSize,
1643 providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
1645 errorString = QQuickPixmap::tr(
"Error decoding: %1").arg(url.toString());
1646 if (f.fileName() != localFile)
1647 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1651 QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
1653 if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
1654 providerOptions, &appliedTransform, frame, devicePixelRatio)) {
1656 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
1657 providerOptions, appliedTransform, frame, frameCount);
1658 }
else if (f.fileName() != localFile) {
1659 errorString += QString::fromLatin1(
" (%1)").arg(f.fileName());
1663 errorString = QQuickPixmap::tr(
"Cannot open: %1").arg(url.toString());
1665 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, errorString);
1676QQuickPixmap::QQuickPixmap()
1681QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url)
1687QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url, Options options)
1690 load(engine, url, options);
1693QQuickPixmap::QQuickPixmap(QQmlEngine *engine,
const QUrl &url,
const QRect ®ion,
const QSize &size)
1696 load(engine, url, region, size);
1699QQuickPixmap::QQuickPixmap(
const QUrl &url,
const QImage &image)
1701 d =
new QQuickPixmapData(url,
new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
1702 QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform);
1706QQuickPixmap::~QQuickPixmap()
1714bool QQuickPixmap::isNull()
const
1716 return d ==
nullptr;
1719bool QQuickPixmap::isReady()
const
1721 return status() == Ready;
1724bool QQuickPixmap::isError()
const
1726 return status() == Error;
1729bool QQuickPixmap::isLoading()
const
1731 return status() == Loading;
1734QString QQuickPixmap::error()
const
1737 return d->errorString;
1742QQuickPixmap::Status QQuickPixmap::status()
const
1745 return d->pixmapStatus;
1750const QUrl &QQuickPixmap::url()
const
1755 return nullPixmap()->url;
1758const QSize &QQuickPixmap::implicitSize()
const
1761 return d->implicitSize;
1763 return nullPixmap()->size;
1766const QSize &QQuickPixmap::requestSize()
const
1769 return d->requestSize;
1771 return nullPixmap()->size;
1774const QRect &QQuickPixmap::requestRegion()
const
1777 return d->requestRegion;
1779 return nullPixmap()->region;
1782QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform()
const
1785 return d->appliedTransform;
1787 return QQuickImageProviderOptions::UsePluginDefaultTransform;
1790int QQuickPixmap::frameCount()
const
1793 return d->frameCount;
1797QQuickTextureFactory *QQuickPixmap::textureFactory()
const
1800 return d->textureFactory;
1805QImage QQuickPixmap::image()
const
1807 if (d && d->textureFactory)
1808 return d->textureFactory->image();
1812void QQuickPixmap::setImage(
const QImage &p)
1819 d =
new QQuickPixmapData(QQuickTextureFactory::textureFactoryForImage(p));
1823void QQuickPixmap::setPixmap(
const QQuickPixmap &other)
1837int QQuickPixmap::width()
const
1839 if (d && d->textureFactory)
1840 return d->textureFactory->textureSize().width();
1845int QQuickPixmap::height()
const
1847 if (d && d->textureFactory)
1848 return d->textureFactory->textureSize().height();
1853QRect QQuickPixmap::rect()
const
1855 if (d && d->textureFactory)
1856 return QRect(QPoint(), d->textureFactory->textureSize());
1861void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url)
1863 load(engine, url, QRect(), QSize(), QQuickPixmap::Cache);
1866void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url, QQuickPixmap::Options options)
1868 load(engine, url, QRect(), QSize(), options);
1871void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize)
1873 load(engine, url, requestRegion, requestSize, QQuickPixmap::Cache);
1876void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize, QQuickPixmap::Options options)
1878 load(engine, url, requestRegion, requestSize, options, QQuickImageProviderOptions());
1881void QQuickPixmap::load(QQmlEngine *engine,
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
1882 QQuickPixmap::Options options,
const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount,
1883 qreal devicePixelRatio)
1890 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1891 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1893 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1894 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1897 QQuickPixmap::Options orgOptions = options;
1900 options |= QQuickPixmap::Cache;
1906 if (url.scheme() == itemGrabberScheme) {
1909 if (requestSize != dummySize)
1910 qWarning() <<
"Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead.";
1911 const QQuickPixmapKey grabberKey = { &url, &dummyRegion, &dummySize, 0, QQuickImageProviderOptions() };
1912 iter = store->m_cache.find(grabberKey);
1913 }
else if (options & QQuickPixmap::Cache)
1914 iter = store->m_cache.find(key);
1916 if (iter == store->m_cache.end()) {
1922 if (url.scheme() == QLatin1String(
"image")) {
1923 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
1924 if (
auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
1925 const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps);
1926 if (!threadedPixmaps && provider->imageType() == QQuickImageProvider::Pixmap) {
1928 options &= ~QQuickPixmap::Asynchronous;
1929 }
else if (provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading) {
1930 options |= QQuickPixmap::Asynchronous;
1935 if (!(options & QQuickPixmap::Asynchronous)) {
1937 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
1938 d = createPixmapDataSync(engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
1940 PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
1941 if (options & QQuickPixmap::Cache)
1944 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1949 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1954 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1955 QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
1956 if (options & QQuickPixmap::Cache)
1959 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1962 QQuickPixmapReader::readerMutex.lock();
1963 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
1964 d->reply = reader->getImage(d);
1965 reader->startJob(d->reply);
1966 QQuickPixmapReader::readerMutex.unlock();
1970 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame;
1975
1976
1977
1978
1979
1980
1981void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device,
const QUrl &url,
1982 const QRect &requestRegion,
const QSize &requestSize,
1983 const QQuickImageProviderOptions &providerOptions,
int frame,
int frameCount)
1986 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1987 QQuickPixmapCache *store = QQuickPixmapCache::instance();
1988 QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
1989 QMutexLocker locker(&store->m_cacheMutex);
1990 iter = store->m_cache.find(key);
1991 if (iter == store->m_cache.end()) {
1996 d =
new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1997 QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
1998 d->specialDevice = device;
1999 d->fromSpecialDevice =
true;
2002 QQuickPixmapReader::readerMutex.lock();
2003 QQuickPixmapReader *reader = QQuickPixmapReader::instance(engine);
2004 d->reply = reader->getImage(d);
2006 QObject::connect(d->reply, &QQuickPixmapReply::destroyed, store, [oldD]() {
2008 }, Qt::QueuedConnection);
2010 reader->startJob(d->reply);
2011 QQuickPixmapReader::readerMutex.unlock();
2015 qCDebug(lcImg) <<
"loaded from cache" << url <<
"frame" << frame <<
"refCount" << d->refCount;
2022void QQuickPixmap::clear()
2030void QQuickPixmap::clear(QObject *obj)
2034 QObject::disconnect(d->reply,
nullptr, obj,
nullptr);
2040bool QQuickPixmap::isCached(
const QUrl &url,
const QRect &requestRegion,
const QSize &requestSize,
2041 const int frame,
const QQuickImageProviderOptions &options)
2043 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, options };
2044 QQuickPixmapCache *store = QQuickPixmapCache::instance();
2046 return store->m_cache.contains(key);
2049bool QQuickPixmap::isScalableImageFormat(
const QUrl &url)
2051 if (url.scheme() ==
"image"_L1)
2054 const QString stringUrl = url.path(QUrl::PrettyDecoded);
2055 return stringUrl.endsWith(
"svg"_L1)
2056 || stringUrl.endsWith(
"svgz"_L1)
2057 || stringUrl.endsWith(
"pdf"_L1);
2060bool QQuickPixmap::connectFinished(QObject *object,
const char *method)
2062 if (!d || !d->reply) {
2063 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2067 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
2070bool QQuickPixmap::connectFinished(QObject *object,
int method)
2072 if (!d || !d->reply) {
2073 qWarning(
"QQuickPixmap: connectFinished() called when not loading.");
2077 return QMetaObject::connect(d->reply, QQuickPixmapReply::finishedMethodIndex, object, method);
2080bool QQuickPixmap::connectDownloadProgress(QObject *object,
const char *method)
2082 if (!d || !d->reply) {
2083 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2087 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object,
2091bool QQuickPixmap::connectDownloadProgress(QObject *object,
int method)
2093 if (!d || !d->reply) {
2094 qWarning(
"QQuickPixmap: connectDownloadProgress() called when not loading.");
2098 return QMetaObject::connect(d->reply, QQuickPixmapReply::downloadProgressMethodIndex, object,
2102QColorSpace QQuickPixmap::colorSpace()
const
2104 if (!d || !d->textureFactory)
2105 return QColorSpace();
2106 return d->textureFactory->image().colorSpace();
2111#include <qquickpixmapcache.moc>
2113#include "moc_qquickpixmap_p.cpp"
2114#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 &rs, const QQuickImageProviderOptions &po, const QString &e)
QQuickImageProviderOptions::AutoTransform appliedTransform
QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1)
QColorSpace targetColorSpace
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)
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)
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
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