Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickpixmapcache.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtQuick/private/qquickpixmapcache_p.h>
5#include <QtQuick/private/qquickimageprovider_p.h>
6#include <QtQuick/private/qquickprofiler_p.h>
7#include <QtQuick/private/qsgcontext_p.h>
8#include <QtQuick/private/qsgrenderer_p.h>
9#include <QtQuick/private/qsgtexturereader_p.h>
10#include <QtQuick/qquickwindow.h>
11
12#include <QtGui/private/qguiapplication_p.h>
13#include <QtGui/private/qimage_p.h>
14#include <QtGui/qpa/qplatformintegration.h>
15#include <QtGui/qimagereader.h>
16#include <QtGui/qpixmapcache.h>
17
18#include <QtQml/private/qqmlglobal_p.h>
19#include <QtQml/private/qqmlengine_p.h>
20#include <QtQml/qqmlfile.h>
21
22#include <QtCore/private/qobject_p.h>
23#include <QtCore/qcoreapplication.h>
24#include <QtCore/qhash.h>
25#include <QtCore/qfile.h>
26#include <QtCore/qthread.h>
27#include <QtCore/qmutex.h>
28#include <QtCore/qbuffer.h>
29#include <QtCore/qdebug.h>
30#include <QtCore/qmetaobject.h>
31#include <QtCore/qscopeguard.h>
32
33#if QT_CONFIG(qml_network)
34#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
35#include <QtNetwork/qnetworkreply.h>
36#include <QtNetwork/qsslerror.h>
37#endif
38
39#include <private/qdebug_p.h>
40
41#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8
42
43// After QQuickPixmapCache::unreferencePixmap() it may get deleted via a timer in 30 seconds
44#define CACHE_EXPIRE_TIME 30
45
46// How many (1/4) of the unreferenced pixmaps to delete in QQuickPixmapCache::timerEvent()
47#define CACHE_REMOVAL_FRACTION 4
48
49#define PIXMAP_PROFILE(Code) Q_QUICK_PROFILE(QQuickProfiler::ProfilePixmapCache, Code)
50
51#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
52# define USE_THREADED_DOWNLOAD 1
53#else
54# define USE_THREADED_DOWNLOAD 0
55#endif
56
58
59using namespace Qt::Literals::StringLiterals;
60
62
63#if defined(QT_DEBUG) && QT_CONFIG(thread)
64class ThreadAffinityMarker
65{
66public:
67 ThreadAffinityMarker() { attachToCurrentThread(); }
68
69 void assertOnAssignedThread()
70 {
71 QMutexLocker locker(&m_mutex);
72 if (!m_assignedThread)
73 attachToCurrentThread();
74 Q_ASSERT_X(m_assignedThread == QThread::currentThreadId(), Q_FUNC_INFO,
75 "Running on a wrong thread!");
76 }
77
78 void detachFromCurrentThread()
79 {
80 QMutexLocker locker(&m_mutex);
81 m_assignedThread = nullptr;
82 }
83
84 void attachToCurrentThread() { m_assignedThread = QThread::currentThreadId(); }
85
86private:
87 Qt::HANDLE m_assignedThread;
88 QMutex m_mutex;
89};
90# define Q_THREAD_AFFINITY_MARKER(x) ThreadAffinityMarker x
91# define Q_ASSERT_CALLED_ON_VALID_THREAD(x) x.assertOnAssignedThread()
92# define Q_DETACH_THREAD_AFFINITY_MARKER(x) x.detachFromCurrentThread()
93#else
94# define Q_THREAD_AFFINITY_MARKER(x)
95# define Q_ASSERT_CALLED_ON_VALID_THREAD(x)
96# define Q_DETACH_THREAD_AFFINITY_MARKER(x)
97#endif
98
100
101Q_LOGGING_CATEGORY(lcImg, "qt.quick.image")
102
103
107static int cache_limit = 2048 * 1024;
108
109static inline QString imageProviderId(const QUrl &url)
110{
111 return url.host();
112}
113
114static inline QString imageId(const QUrl &url)
115{
117}
118
120{
122 || image.format() == QImage::Format_RGB32) {
123 im = image;
124 } else {
125 im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
126 }
127 size = im.size();
128}
129
130
132{
133 QSGTexture *t = window->createTextureFromImage(im, QQuickWindow::TextureCanUseAtlas);
134 static bool transient = qEnvironmentVariableIsSet("QSG_TRANSIENT_IMAGES");
135 if (transient)
136 const_cast<QQuickDefaultTextureFactory *>(this)->im = QImage();
137 return t;
138}
139
141class QQuickPixmapData;
143{
145public:
147
150
152 QQmlEngine *engineForReader; // always access reader inside readerMutex
156
159
172
173
175 void finished();
177
178protected:
179 bool event(QEvent *event) override;
180
181private:
182 Q_DISABLE_COPY(QQuickPixmapReply)
183
184public:
187};
188
193{
195public:
199
201
206
207public slots:
210private slots:
211 void networkRequestDone();
212private:
213 Q_DISABLE_COPY(ReaderThreadExecutionEnforcer)
214 bool event(QEvent *e) override;
215
216 QQuickPixmapReader *reader;
217};
218
219class QQuickPixmapData;
221{
223public:
226
228 void cancel(QQuickPixmapReply *rep);
229
230 static QQuickPixmapReader *instance(QQmlEngine *engine);
232 void startJob(QQuickPixmapReply *job);
233
234protected:
235 void run() override;
236
237private:
238 Q_DISABLE_COPY(QQuickPixmapReader)
240 void processJobs();
241 void processJob(QQuickPixmapReply *, const QUrl &, const QString &, QQuickImageProvider::ImageType, const QSharedPointer<QQuickImageProvider> &);
242#if QT_CONFIG(qml_network)
243 void networkRequestDone(QNetworkReply *);
244#endif
245 void asyncResponseFinished(QQuickImageResponse *);
246
247 QList<QQuickPixmapReply*> jobs;
248 QList<QQuickPixmapReply *> cancelledJobs;
250
251#if QT_CONFIG(quick_pixmap_cache_threaded_download)
255 ReaderThreadExecutionEnforcer *readerThreadExecutionEnforcer()
256 {
257 return runLoopReaderThreadExecutionEnforcer;
258 }
259 QObject *eventLoopQuitHack;
261 ReaderThreadExecutionEnforcer *runLoopReaderThreadExecutionEnforcer = nullptr;
262#else
266 ReaderThreadExecutionEnforcer *readerThreadExecutionEnforcer()
267 {
268 return ownedReaderThreadExecutionEnforcer.get();
269 }
270 std::unique_ptr<ReaderThreadExecutionEnforcer> ownedReaderThreadExecutionEnforcer;
271#endif
272
273#if QT_CONFIG(qml_network)
274 QNetworkAccessManager *networkAccessManager();
275 QNetworkAccessManager *accessManager;
276 QHash<QNetworkReply*,QQuickPixmapReply*> networkJobs;
277#endif
278 QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses;
279
280 Q_THREAD_AFFINITY_MARKER(m_creatorThreadAffinityMarker);
281 Q_THREAD_AFFINITY_MARKER(m_readerThreadAffinityMarker);
282
283 static int replyDownloadProgressMethodIndex;
284 static int replyFinishedMethodIndex;
285 static int downloadProgressMethodIndex;
286 static int threadNetworkRequestDoneMethodIndex;
287 static QHash<QQmlEngine *,QQuickPixmapReader*> readers;
288public:
290};
291
292#if QT_CONFIG(quick_pixmap_cache_threaded_download)
293# define PIXMAP_READER_LOCK() QMutexLocker locker(&mutex)
294#else
295# define PIXMAP_READER_LOCK()
296#endif
297
299
304{
305public:
306 QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &rs,
307 const QQuickImageProviderOptions &po, const QString &e)
310 providerOptions(po), appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform),
313#ifdef Q_OS_WEBOS
314 , storeToCache(true)
315#endif
316 {
317 }
318
319 QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po,
323 providerOptions(po), appliedTransform(aTransform),
326#ifdef Q_OS_WEBOS
327 , storeToCache(true)
328#endif
329 {
330 }
331
333 const QSize &s, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po,
337 providerOptions(po), appliedTransform(aTransform),
340#ifdef Q_OS_WEBOS
341 , storeToCache(true)
342#endif
343 {
344 }
345
348 appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform),
351#ifdef Q_OS_WEBOS
352 , storeToCache(true)
353#endif
354 {
355 if (texture)
356 requestSize = implicitSize = texture->textureSize();
357 }
358
360 {
361 delete textureFactory;
362 }
363
364 int cost() const;
365 void addref();
366 void release(QQuickPixmapCache *store = nullptr);
367 void addToCache();
368 void removeFromCache(QQuickPixmapCache *store = nullptr);
369
372 int frame;
373
374 bool inCache:1;
375
385
387
388 // actual image data, after loading
390
392
393 // prev/next pointers to form a linked list for dereferencing pixmaps that are currently unused
394 // (those get lazily deleted in QQuickPixmapCache::shrinkCache())
398
399#ifdef Q_OS_WEBOS
400 bool storeToCache;
401#endif
402
403private:
404 Q_DISABLE_COPY(QQuickPixmapData)
405};
406
409
410// XXX
411QHash<QQmlEngine *,QQuickPixmapReader*> QQuickPixmapReader::readers;
413
414int QQuickPixmapReader::replyDownloadProgressMethodIndex = -1;
415int QQuickPixmapReader::replyFinishedMethodIndex = -1;
416int QQuickPixmapReader::downloadProgressMethodIndex = -1;
417int QQuickPixmapReader::threadNetworkRequestDoneMethodIndex = -1;
418
420 const QSize &implicitSize, QQuickTextureFactory *factory)
421{
422 loading = false;
423 QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, factory));
424}
425
427 : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), textureFactory(factory)
428{
429}
430
432{
433 delete textureFactory;
434}
435
436#if QT_CONFIG(qml_network)
437QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
438{
439 if (!accessManager) {
440 Q_ASSERT(readerThreadExecutionEnforcer());
441 accessManager = QQmlEnginePrivate::get(engine)->createNetworkAccessManager(
442 readerThreadExecutionEnforcer());
443 }
444 return accessManager;
445}
446#endif
447
449{
450 // If the image
451 if (image->hasAlphaChannel() && image->data_ptr()
452 && !image->data_ptr()->checkForAlphaPixels()) {
453 switch (image->format()) {
456 if (image->data_ptr()->convertInPlace(QImage::Format_RGBX8888, Qt::AutoColor))
457 break;
458
459 *image = image->convertToFormat(QImage::Format_RGBX8888);
460 break;
462 if (image->data_ptr()->convertInPlace(QImage::Format_BGR30, Qt::AutoColor))
463 break;
464
465 *image = image->convertToFormat(QImage::Format_BGR30);
466 break;
468 if (image->data_ptr()->convertInPlace(QImage::Format_RGB30, Qt::AutoColor))
469 break;
470
471 *image = image->convertToFormat(QImage::Format_RGB30);
472 break;
473 default:
474 if (image->data_ptr()->convertInPlace(QImage::Format_RGB32, Qt::AutoColor))
475 break;
476
477 *image = image->convertToFormat(QImage::Format_RGB32);
478 break;
479 }
480 }
481}
482
483static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int *frameCount,
485 QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr, int frame = 0,
486 qreal devicePixelRatio = 1.0)
487{
488 QImageReader imgio(dev);
491 else if (appliedTransform)
493
494 if (frame < imgio.imageCount())
495 imgio.jumpToImage(frame);
496
497 if (frameCount)
498 *frameCount = imgio.imageCount();
499
500 QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions, devicePixelRatio);
501 if (scSize.isValid())
502 imgio.setScaledSize(scSize);
503 if (!requestRegion.isNull())
504 imgio.setScaledClipRect(requestRegion);
505 const QSize originalSize = imgio.size();
506 qCDebug(lcImg) << url << "frame" << frame << "of" << imgio.imageCount()
507 << "requestRegion" << requestRegion << "QImageReader size" << originalSize << "-> scSize" << scSize;
508
509 if (impsize)
510 *impsize = originalSize;
511
512 if (imgio.read(image)) {
514 if (impsize && impsize->width() < 0)
515 *impsize = image->size();
517 if (image->colorSpace().isValid())
518 image->convertToColorSpace(providerOptions.targetColorSpace());
519 else
520 image->setColorSpace(providerOptions.targetColorSpace());
521 }
522 return true;
523 } else {
524 if (errorString)
525 *errorString = QQuickPixmap::tr("Error decoding: %1: %2").arg(url.toString())
526 .arg(imgio.errorString());
527 return false;
528 }
529}
530
531static QStringList fromLatin1List(const QList<QByteArray> &list)
532{
534 res.reserve(list.size());
535 for (const QByteArray &item : list)
537 return res;
538}
539
541{
542public:
544 {
545 delete QSGContext::createTextureFactoryFromImage(QImage()); // Force init of backend data
546 hasOpenGL = QQuickWindow::sceneGraphBackend().isEmpty(); // i.e. default
547 QList<QByteArray> list;
548 if (hasOpenGL)
551 fileSuffixes = fromLatin1List(list);
552 }
555private:
556 Q_DISABLE_COPY(BackendSupport)
557};
559
561{
562 // Do nothing if given filepath exists or already has a suffix
563 QFileInfo fi(localFile);
564 if (!fi.suffix().isEmpty() || fi.exists())
565 return localFile;
566
567 QString tryFile = localFile + QStringLiteral(".xxxx");
568 const int suffixIdx = localFile.size() + 1;
569 for (const QString &suffix : backendSupport()->fileSuffixes) {
570 tryFile.replace(suffixIdx, 10, suffix);
571 if (QFileInfo::exists(tryFile))
572 return tryFile;
573 }
574 return localFile;
575}
576
578: QThread(eng), engine(eng)
579#if QT_CONFIG(qml_network)
580, accessManager(nullptr)
581#endif
582{
583 Q_DETACH_THREAD_AFFINITY_MARKER(m_readerThreadAffinityMarker);
584#if QT_CONFIG(quick_pixmap_cache_threaded_download)
585 eventLoopQuitHack = new QObject;
586 eventLoopQuitHack->moveToThread(this);
589#else
590 run(); // Call nonblocking run for ourselves.
591#endif
592}
593
595{
596 Q_ASSERT_CALLED_ON_VALID_THREAD(m_creatorThreadAffinityMarker);
597
598 readerMutex.lock();
599 readers.remove(engine);
600 readerMutex.unlock();
601
602 {
604 // manually cancel all outstanding jobs.
605 for (QQuickPixmapReply *reply : std::as_const(jobs)) {
606 if (reply->data && reply->data->reply == reply)
607 reply->data->reply = nullptr;
608 delete reply;
609 }
610 jobs.clear();
611#if QT_CONFIG(qml_network)
612 const auto cancelJob = [this](QQuickPixmapReply *reply) {
613 if (reply->loading) {
614 cancelledJobs.append(reply);
615 reply->data = nullptr;
616 }
617 };
618
619 for (auto *reply : std::as_const(networkJobs))
620 cancelJob(reply);
621
622 for (auto *reply : std::as_const(asyncResponses))
623 cancelJob(reply);
624#endif
625#if !QT_CONFIG(quick_pixmap_cache_threaded_download)
626 // In this case we won't be waiting, but we are on the correct thread already, so we can
627 // perform housekeeping synchronously now.
628 processJobs();
629#else // QT_CONFIG(quick_pixmap_cache_threaded_download) is true
630 // Perform housekeeping on all the jobs cancelled above soon...
631 if (readerThreadExecutionEnforcer())
632 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
633#endif
634 }
635
636#if QT_CONFIG(quick_pixmap_cache_threaded_download)
637 // ... schedule stopping of this thread via the eventLoopQuitHack (processJobs scheduled above
638 // will run first) ...
639 eventLoopQuitHack->deleteLater();
640 // ... and wait() for it all to finish, as the thread will only quit after eventLoopQuitHack
641 // has been deleted.
642 wait();
643#endif
644
645#if QT_CONFIG(qml_network)
646 // While we've been waiting, the other thread may have added
647 // more replies. No one will care about them anymore.
648
649 auto deleteReply = [](QQuickPixmapReply *reply) {
650 if (reply->data && reply->data->reply == reply)
651 reply->data->reply = nullptr;
652 delete reply;
653 };
654
655 for (QQuickPixmapReply *reply : std::as_const(networkJobs))
656 deleteReply(reply);
657
658 for (QQuickPixmapReply *reply : std::as_const(asyncResponses))
659 deleteReply(reply);
660
661 networkJobs.clear();
662 asyncResponses.clear();
663#endif
664}
665
666#if QT_CONFIG(qml_network)
667void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
668{
669 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
670
671 QQuickPixmapReply *job = networkJobs.take(reply);
672
673 if (job) {
676 QString errorString;
677 QSize readSize;
678 QQuickTextureFactory *factory = nullptr;
679 if (reply->error()) {
681 errorString = reply->errorString();
682 } else {
684 QBuffer buff(&all);
685 buff.open(QIODevice::ReadOnly);
686 QSGTextureReader texReader(&buff, reply->url().fileName());
687 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
688 factory = texReader.read();
689 if (factory) {
690 readSize = factory->textureSize();
691 } else {
693 errorString = QQuickPixmap::tr("Error decoding: %1").arg(reply->url().toString());
694 }
695 } else {
696 int frameCount;
697 int const frame = job->data ? job->data->frame : 0;
698 if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount,
699 job->requestRegion, job->requestSize, job->providerOptions, nullptr, frame))
701 else if (job->data)
702 job->data->frameCount = frameCount;
703 }
704 }
705 // send completion event to the QQuickPixmapReply
706 if (!factory)
708
710 if (!cancelledJobs.contains(job))
711 job->postReply(error, errorString, readSize, factory);
712 }
714
715 // kick off event loop again in case we have dropped below max request count
716 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
717}
718#endif // qml_network
719
720void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response)
721{
722 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
723
724 QQuickPixmapReply *job = asyncResponses.take(response);
725
726 if (job) {
727 QQuickTextureFactory *t = nullptr;
730 if (!response->errorString().isEmpty()) {
732 errorString = response->errorString();
733 } else {
734 t = response->textureFactory();
735 }
736
738 if (!cancelledJobs.contains(job))
739 job->postReply(error, errorString, t ? t->textureSize() : QSize(), t);
740 else
741 delete t;
742 }
743 response->deleteLater();
744
745 // kick off event loop again in case we have dropped below max request count
746 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
747}
748
750
756
758{
759 switch (e->type()) {
761 reader->processJobs();
762 return true;
763 default:
764 return QObject::event(e);
765 }
766}
767
768void ReaderThreadExecutionEnforcer::networkRequestDone()
769{
770#if QT_CONFIG(qml_network)
771 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
772 reader->networkRequestDone(reply);
773#endif
774}
775
777{
778 reader->asyncResponseFinished(response);
779}
780
786
787void QQuickPixmapReader::processJobs()
788{
789 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
790
792 while (true) {
793 if (cancelledJobs.isEmpty() && jobs.isEmpty())
794 return; // Nothing else to do
795
796 // Clean cancelled jobs
797 if (!cancelledJobs.isEmpty()) {
798 for (int i = 0; i < cancelledJobs.size(); ++i) {
799 QQuickPixmapReply *job = cancelledJobs.at(i);
800#if QT_CONFIG(qml_network)
801 QNetworkReply *reply = networkJobs.key(job, 0);
802 if (reply) {
803 networkJobs.remove(reply);
804 if (reply->isRunning()) {
805 // cancel any jobs already started
806 reply->close();
807 }
808 } else {
809 QQuickImageResponse *asyncResponse = asyncResponses.key(job);
810 if (asyncResponse) {
811 asyncResponses.remove(asyncResponse);
812 asyncResponse->cancel();
813 }
814 }
815 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url));
816#endif
817 // deleteLater, since not owned by this thread
818 job->deleteLater();
819 }
820 cancelledJobs.clear();
821 }
822
823 if (!jobs.isEmpty()) {
824 // Find a job we can use
825 bool usableJob = false;
826 for (int i = jobs.size() - 1; !usableJob && i >= 0; i--) {
827 QQuickPixmapReply *job = jobs.at(i);
828 const QUrl url = job->url;
829 QString localFile;
831 QSharedPointer<QQuickImageProvider> provider;
832
833 if (url.scheme() == QLatin1String("image")) {
834 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
835 provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>();
836 if (provider)
837 imageType = provider->imageType();
838
839 usableJob = true;
840 } else {
842 usableJob = !localFile.isEmpty()
843#if QT_CONFIG(qml_network)
844 || networkJobs.size() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
845#endif
846 ;
847 }
848
849 if (usableJob) {
850 jobs.removeAt(i);
851
852 job->loading = true;
853
854 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
855
856#if QT_CONFIG(quick_pixmap_cache_threaded_download)
857 locker.unlock();
858 auto relockMutexGuard = qScopeGuard(([&locker]() {
859 locker.relock();
860 }));
861#endif
862 processJob(job, url, localFile, imageType, provider);
863 }
864 }
865
866 if (!usableJob)
867 return;
868 }
869 }
870}
871
872void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, const QString &localFile,
873 QQuickImageProvider::ImageType imageType, const QSharedPointer<QQuickImageProvider> &provider)
874{
875 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
876
877 // fetch
878 if (url.scheme() == QLatin1String("image")) {
879 // Use QQuickImageProvider
880 QSize readSize;
881
882 if (imageType == QQuickImageProvider::Invalid) {
883 QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString());
885 if (!cancelledJobs.contains(runningJob))
886 runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize, nullptr);
887 return;
888 }
889
890 // This is safe because we ensure that provider does outlive providerV2 and it does not escape the function
892
893 switch (imageType) {
895 {
896 // Already handled
897 break;
898 }
899
901 {
903 if (providerV2) {
904 image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
905 } else {
906 image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize);
907 }
910 if (image.isNull()) {
911 errorCode = QQuickPixmapReply::Loading;
912 errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString());
913 }
915 if (!cancelledJobs.contains(runningJob)) {
916 runningJob->postReply(errorCode, errorStr, readSize,
918 }
919 break;
920 }
921
923 {
925 if (providerV2) {
926 pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
927 } else {
928 pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize);
929 }
932 if (pixmap.isNull()) {
933 errorCode = QQuickPixmapReply::Loading;
934 errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString());
935 }
936
938 if (!cancelledJobs.contains(runningJob)) {
939 runningJob->postReply(
940 errorCode, errorStr, readSize,
942 }
943 break;
944 }
945
947 {
949 if (providerV2) {
950 t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions);
951 } else {
952 t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize);
953 }
956 if (!t) {
957 errorCode = QQuickPixmapReply::Loading;
958 errorStr = QQuickPixmap::tr("Failed to get texture from provider: %1").arg(url.toString());
959 }
961 if (!cancelledJobs.contains(runningJob))
962 runningJob->postReply(errorCode, errorStr, readSize, t);
963 else
964 delete t;
965 break;
966 }
967
969 {
970 QQuickImageResponse *response;
971 if (providerV2) {
972 response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions);
973 } else {
974 QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider.get());
975 response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
976 }
977
978 {
979 QObject::connect(response, &QQuickImageResponse::finished, readerThreadExecutionEnforcer(),
981 // as the response object can outlive the provider QSharedPointer, we have to extend the pointee's lifetime by that of the response
982 // we do this by capturing a copy of the QSharedPointer in a lambda, and dropping it once the lambda has been called
983 auto provider_copy = provider; // capturing provider would capture it as a const reference, and copy capture with initializer is only available in C++14
984 QObject::connect(response, &QQuickImageResponse::destroyed, response, [provider_copy]() {
985 // provider_copy will be deleted when the connection gets deleted
986 });
987 }
988 // Might be that the async provider was so quick it emitted the signal before we
989 // could connect to it.
990 //
991 // loadAcquire() synchronizes-with storeRelease() in QQuickImageResponsePrivate::_q_finished():
992 if (static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
993 QMetaObject::invokeMethod(readerThreadExecutionEnforcer(), "asyncResponseFinished",
995 Q_ARG(QQuickImageResponse *, response));
996 }
997
998 asyncResponses.insert(response, runningJob);
999 break;
1000 }
1001 }
1002
1003 } else {
1004 if (!localFile.isEmpty()) {
1005 // Image is local - load/decode immediately
1006 QImage image;
1009 QSize readSize;
1010
1011 if (runningJob->data && runningJob->data->specialDevice) {
1012 int frameCount;
1013 if (!readImage(url, runningJob->data->specialDevice, &image, &errorStr, &readSize, &frameCount,
1014 runningJob->requestRegion, runningJob->requestSize,
1015 runningJob->providerOptions, nullptr, runningJob->data->frame)) {
1016 errorCode = QQuickPixmapReply::Loading;
1017 } else if (runningJob->data) {
1018 runningJob->data->frameCount = frameCount;
1019 }
1020 } else {
1021 QFile f(existingImageFileForPath(localFile));
1022 if (f.open(QIODevice::ReadOnly)) {
1023 QSGTextureReader texReader(&f, localFile);
1024 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1025 QQuickTextureFactory *factory = texReader.read();
1026 if (factory) {
1027 readSize = factory->textureSize();
1028 } else {
1029 errorStr = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
1030 if (f.fileName() != localFile)
1031 errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
1032 errorCode = QQuickPixmapReply::Decoding;
1033 }
1035 if (!cancelledJobs.contains(runningJob))
1036 runningJob->postReply(errorCode, errorStr, readSize, factory);
1037 return;
1038 } else {
1039 int frameCount;
1040 int const frame = runningJob->data ? runningJob->data->frame : 0;
1041 if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount,
1042 runningJob->requestRegion, runningJob->requestSize,
1043 runningJob->providerOptions, nullptr, frame)) {
1044 errorCode = QQuickPixmapReply::Loading;
1045 if (f.fileName() != localFile)
1046 errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
1047 } else if (runningJob->data) {
1048 runningJob->data->frameCount = frameCount;
1049 }
1050 }
1051 } else {
1052 errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
1053 errorCode = QQuickPixmapReply::Loading;
1054 }
1055 }
1057 if (!cancelledJobs.contains(runningJob)) {
1058 runningJob->postReply(errorCode, errorStr, readSize,
1060 }
1061 } else {
1062#if QT_CONFIG(qml_network)
1063 // Network resource
1064 QNetworkRequest req(url);
1066 QNetworkReply *reply = networkAccessManager()->get(req);
1067
1068 QMetaObject::connect(reply, replyDownloadProgressMethodIndex, runningJob,
1069 downloadProgressMethodIndex);
1070 QMetaObject::connect(reply, replyFinishedMethodIndex, readerThreadExecutionEnforcer(),
1071 threadNetworkRequestDoneMethodIndex);
1072
1073 networkJobs.insert(reply, runningJob);
1074#else
1075// Silently fail if compiled with no_network
1076#endif
1077 }
1078 }
1079}
1080
1082{
1083 // XXX NOTE: must be called within readerMutex locking.
1084 QQuickPixmapReader *reader = readers.value(engine);
1085 if (!reader) {
1086 reader = new QQuickPixmapReader(engine);
1087 readers.insert(engine, reader);
1088 }
1089
1090 return reader;
1091}
1092
1094{
1095 // XXX NOTE: must be called within readerMutex locking.
1096 return readers.value(engine, 0);
1097}
1098
1105
1107{
1109 jobs.append(job);
1110 if (readerThreadExecutionEnforcer())
1111 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
1112}
1113
1115{
1117 if (reply->loading) {
1118 cancelledJobs.append(reply);
1119 reply->data = nullptr;
1120 // XXX
1121 if (readerThreadExecutionEnforcer())
1122 readerThreadExecutionEnforcer()->processJobsOnReaderThreadLater();
1123 } else {
1124 // If loading was started (reply removed from jobs) but the reply was never processed
1125 // (otherwise it would have deleted itself) we need to profile an error.
1126 if (jobs.removeAll(reply) == 0) {
1127 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(reply->url));
1128 }
1129 delete reply;
1130 }
1131}
1132
1134{
1135 Q_ASSERT_CALLED_ON_VALID_THREAD(m_readerThreadAffinityMarker);
1136
1137 if (replyDownloadProgressMethodIndex == -1) {
1138#if QT_CONFIG(qml_network)
1139 replyDownloadProgressMethodIndex =
1141 replyFinishedMethodIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
1142 const QMetaObject *ir = &ReaderThreadExecutionEnforcer::staticMetaObject;
1143 threadNetworkRequestDoneMethodIndex = ir->indexOfSlot("networkRequestDone()");
1144#endif
1145 downloadProgressMethodIndex =
1147 }
1148
1149#if QT_CONFIG(quick_pixmap_cache_threaded_download)
1150 const auto guard = qScopeGuard([this]() {
1151 // We need to delete the runLoopReaderThreadExecutionEnforcer from the same thread.
1153 delete runLoopReaderThreadExecutionEnforcer;
1154 runLoopReaderThreadExecutionEnforcer = nullptr;
1155 });
1156
1157 {
1159 Q_ASSERT(!runLoopReaderThreadExecutionEnforcer);
1160 runLoopReaderThreadExecutionEnforcer = new ReaderThreadExecutionEnforcer(this);
1161 }
1162
1163 processJobs();
1164 exec();
1165#else
1166 ownedReaderThreadExecutionEnforcer = std::make_unique<ReaderThreadExecutionEnforcer>(this);
1167 processJobs();
1168#endif
1169}
1170
1171inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs)
1172{
1173 return *lhs.url == *rhs.url &&
1174 *lhs.region == *rhs.region &&
1175 *lhs.size == *rhs.size &&
1176 lhs.frame == rhs.frame &&
1177 lhs.options == rhs.options;
1178}
1179
1180inline size_t qHash(const QQuickPixmapKey &key, size_t seed) noexcept
1181{
1182 return qHashMulti(seed, *key.url, *key.region, *key.size, key.frame, key.options.autoTransform());
1183}
1184
1185#ifndef QT_NO_DEBUG_STREAM
1187{
1188 QDebugStateSaver saver(debug);
1189 debug.nospace();
1190 if (!key.url) {
1191 debug << "QQuickPixmapKey(0)";
1192 return debug;
1193 }
1194
1195 debug << "QQuickPixmapKey(" << key.url->toString() << " frame=" << key.frame;
1196 if (!key.region->isEmpty()) {
1197 debug << " region=";
1199 }
1200 if (!key.size->isEmpty()) {
1201 debug << " size=";
1203 }
1204 debug << ')';
1205 return debug;
1206}
1207#endif
1208
1210{
1211 static QQuickPixmapCache self;
1212 return &self;
1213}
1214
1216{
1217 destroyCache();
1218}
1219
1228int QQuickPixmapCache::destroyCache()
1229{
1230 if (m_destroying)
1231 return -1;
1232
1233 m_destroying = true;
1234
1235 // Prevent unreferencePixmap() from assuming it needs to kick
1236 // off the cache expiry timer, as we're shrinking the cache
1237 // manually below after releasing all the pixmaps.
1238 m_timerId = -2;
1239
1240 // unreference all (leaked) pixmaps
1241 int leakedPixmaps = 0;
1242 const auto cache = m_cache; // NOTE: intentional copy (QTBUG-65077); releasing items from the cache modifies m_cache.
1243 for (auto *pixmap : cache) {
1244 auto currRefCount = pixmap->refCount;
1245 if (currRefCount) {
1246 leakedPixmaps++;
1247 qCDebug(lcQsgLeak) << "leaked pixmap: refCount" << pixmap->refCount << pixmap->url << "frame" << pixmap->frame
1248 << "size" << pixmap->requestSize << "region" << pixmap->requestRegion;
1249 while (currRefCount > 0) {
1250 pixmap->release(this);
1251 currRefCount--;
1252 }
1253 }
1254 }
1255
1256 // free all unreferenced pixmaps
1257 while (m_lastUnreferencedPixmap)
1258 shrinkCache(20);
1259
1260 qCDebug(lcQsgLeak, "Number of leaked pixmaps: %i", leakedPixmaps);
1261 return leakedPixmaps;
1262}
1263
1264qsizetype QQuickPixmapCache::referencedCost() const
1265{
1266 qsizetype ret = 0;
1267 QMutexLocker locker(&m_cacheMutex);
1268 for (const auto *pixmap : std::as_const(m_cache)) {
1269 if (pixmap->refCount)
1270 ret += pixmap->cost();
1271 }
1272 return ret;
1273}
1274
1280{
1281 Q_ASSERT(data->prevUnreferenced == nullptr);
1282 Q_ASSERT(data->prevUnreferencedPtr == nullptr);
1283 Q_ASSERT(data->nextUnreferenced == nullptr);
1284
1285 data->nextUnreferenced = m_unreferencedPixmaps;
1286 data->prevUnreferencedPtr = &m_unreferencedPixmaps;
1287 if (!m_destroying) { // the texture factories may have been cleaned up already.
1288 m_unreferencedCost += data->cost();
1289 qCDebug(lcImg) << data->url << "had cost" << data->cost() << "of total unreferenced" << m_unreferencedCost;
1290 }
1291
1292 m_unreferencedPixmaps = data;
1293 if (m_unreferencedPixmaps->nextUnreferenced) {
1294 m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps;
1295 m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced;
1296 }
1297
1298 if (!m_lastUnreferencedPixmap)
1299 m_lastUnreferencedPixmap = data;
1300
1301 shrinkCache(-1); // Shrink the cache in case it has become larger than cache_limit
1302
1303 if (m_timerId == -1 && m_unreferencedPixmaps
1304 && !m_destroying && !QCoreApplication::closingDown()) {
1305 m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000);
1306 }
1307}
1308
1314{
1315 Q_ASSERT(data->prevUnreferencedPtr);
1316
1317 *data->prevUnreferencedPtr = data->nextUnreferenced;
1318 if (data->nextUnreferenced) {
1319 data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr;
1320 data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced;
1321 }
1322 if (m_lastUnreferencedPixmap == data)
1323 m_lastUnreferencedPixmap = data->prevUnreferenced;
1324
1325 data->nextUnreferenced = nullptr;
1326 data->prevUnreferencedPtr = nullptr;
1327 data->prevUnreferenced = nullptr;
1328
1329 m_unreferencedCost -= data->cost();
1330 qCDebug(lcImg) << data->url << "subtracts cost" << data->cost() << "of total" << m_unreferencedCost;
1331}
1332
1337void QQuickPixmapCache::shrinkCache(int remove)
1338{
1339 qCDebug(lcImg) << "reduce unreferenced cost" << m_unreferencedCost << "to less than limit" << cache_limit;
1340 while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) {
1341 QQuickPixmapData *data = m_lastUnreferencedPixmap;
1342 Q_ASSERT(data->nextUnreferenced == nullptr);
1343
1344 *data->prevUnreferencedPtr = nullptr;
1345 m_lastUnreferencedPixmap = data->prevUnreferenced;
1346 data->prevUnreferencedPtr = nullptr;
1347 data->prevUnreferenced = nullptr;
1348
1349 if (!m_destroying) {
1350 remove -= data->cost();
1351 m_unreferencedCost -= data->cost();
1352 }
1353 data->removeFromCache(this);
1354 delete data;
1355 }
1356}
1357
1359{
1360 int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION;
1361
1362 shrinkCache(removalCost);
1363
1364 if (m_unreferencedPixmaps == nullptr) {
1365 killTimer(m_timerId);
1366 m_timerId = -1;
1367 }
1368}
1369
1371{
1372 shrinkCache(m_unreferencedCost);
1373}
1374
1376{
1377 QQuickPixmapCache::instance()->purgeCache();
1378}
1379
1381 : data(d), engineForReader(nullptr), requestRegion(d->requestRegion), requestSize(d->requestSize),
1382 url(d->url), loading(false), providerOptions(d->providerOptions)
1383{
1384 if (finishedMethodIndex == -1) {
1388 }
1389}
1390
1392{
1393 // note: this->data->reply must be set to zero if this->data->reply == this
1394 // but it must be done within mutex locking, to be guaranteed to be safe.
1395}
1396
1398{
1399 if (event->type() == QEvent::User) {
1400
1401 if (data) {
1402 Event *de = static_cast<Event *>(event);
1403 data->pixmapStatus = (de->error == NoError) ? QQuickPixmap::Ready : QQuickPixmap::Error;
1404 if (data->pixmapStatus == QQuickPixmap::Ready) {
1405 data->textureFactory = de->textureFactory;
1406 de->textureFactory = nullptr;
1407 data->implicitSize = de->implicitSize;
1408 PIXMAP_PROFILE(pixmapLoadingFinished(data->url,
1409 data->textureFactory != nullptr && data->textureFactory->textureSize().isValid() ?
1410 data->textureFactory->textureSize() :
1411 (data->requestSize.isValid() ? data->requestSize : data->implicitSize)));
1412 } else {
1413 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(data->url));
1414 data->errorString = de->errorString;
1415 data->removeFromCache(); // We don't continue to cache error'd pixmaps
1416 }
1417
1418 data->reply = nullptr;
1419 emit finished();
1420 } else {
1421 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1422 }
1423
1424 delete this;
1425 return true;
1426 } else {
1427 return QObject::event(event);
1428 }
1429}
1430
1432{
1433 if (textureFactory)
1435 return 0;
1436}
1437
1439{
1440 ++refCount;
1441 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1443 QQuickPixmapCache::instance()->referencePixmap(this);
1444}
1445
1447{
1448 Q_ASSERT(refCount > 0);
1449 --refCount;
1450 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapReferenceCountChanged>(url, refCount));
1451 if (refCount == 0) {
1452 if (reply) {
1453 QQuickPixmapReply *cancelReply = reply;
1454 reply->data = nullptr;
1455 reply = nullptr;
1457 QQuickPixmapReader *reader = QQuickPixmapReader::existingInstance(cancelReply->engineForReader);
1458 if (reader)
1459 reader->cancel(cancelReply);
1461 }
1462
1463 store = store ? store : QQuickPixmapCache::instance();
1465#ifdef Q_OS_WEBOS
1466 && storeToCache
1467#endif
1468 ) {
1469 if (inCache)
1470 store->unreferencePixmap(this);
1471 else
1472 delete this;
1473 } else {
1474 removeFromCache(store);
1475 delete this;
1476 }
1477 }
1478}
1479
1492{
1493 if (!inCache) {
1495 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1496 if (lcImg().isDebugEnabled()) {
1497 qCDebug(lcImg) << "adding" << key << "to total" << QQuickPixmapCache::instance()->m_cache.size();
1498 for (auto it = QQuickPixmapCache::instance()->m_cache.keyBegin(); it != QQuickPixmapCache::instance()->m_cache.keyEnd(); ++it) {
1499 if (*(it->url) == url && it->frame == frame)
1500 qDebug(lcImg) << " similar pre-existing:" << *it;
1501 }
1502 }
1503 QQuickPixmapCache::instance()->m_cache.insert(key, this);
1504 inCache = true;
1505 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1506 url, QQuickPixmapCache::instance()->m_cache.size()));
1507 }
1508}
1509
1511{
1512 if (inCache) {
1513 if (!store)
1516 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1517 store->m_cache.remove(key);
1518 qCDebug(lcImg) << "removed" << key << implicitSize << "; total remaining" << QQuickPixmapCache::instance()->m_cache.size();
1519 inCache = false;
1520 PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
1521 url, store->m_cache.size()));
1522 }
1523}
1524
1526 const QRect &requestRegion, const QSize &requestSize,
1527 const QQuickImageProviderOptions &providerOptions, int frame, bool *ok,
1528 qreal devicePixelRatio)
1529{
1530 if (url.scheme() == QLatin1String("image")) {
1531 QSize readSize;
1532
1535 QSharedPointer<QQuickImageProvider> provider = enginePrivate->imageProvider(imageProviderId(url)).objectCast<QQuickImageProvider>();
1536 // it is safe to use get() as providerV2 does not escape and is outlived by provider
1538 if (provider)
1539 imageType = provider->imageType();
1540
1541 switch (imageType) {
1543 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1544 QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()));
1546 {
1547 QQuickTextureFactory *texture = providerV2 ? providerV2->requestTexture(imageId(url), &readSize, requestSize, providerOptions)
1548 : provider->requestTexture(imageId(url), &readSize, requestSize);
1549 if (texture) {
1550 *ok = true;
1551 return new QQuickPixmapData(url, texture, readSize, requestRegion, requestSize,
1553 }
1554 break;
1555 }
1556
1558 {
1559 QImage image = providerV2 ? providerV2->requestImage(imageId(url), &readSize, requestSize, providerOptions)
1560 : provider->requestImage(imageId(url), &readSize, requestSize);
1561 if (!image.isNull()) {
1562 *ok = true;
1564 readSize, requestRegion, requestSize, providerOptions,
1566 }
1567 break;
1568 }
1570 {
1571 QPixmap pixmap = providerV2 ? providerV2->requestPixmap(imageId(url), &readSize, requestSize, providerOptions)
1572 : provider->requestPixmap(imageId(url), &readSize, requestSize);
1573 if (!pixmap.isNull()) {
1574 *ok = true;
1576 readSize, requestRegion, requestSize, providerOptions,
1578 }
1579 break;
1580 }
1582 {
1583 // Fall through, ImageResponse providers never get here
1584 Q_ASSERT(imageType != QQuickImageProvider::ImageResponse && "Sync call to ImageResponse provider");
1585 }
1586 }
1587
1588 // provider has bad image type, or provider returned null image
1589 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1590 QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()));
1591 }
1592
1594 if (localFile.isEmpty())
1595 return nullptr;
1596
1597 QFile f(existingImageFileForPath(localFile));
1598 QSize readSize;
1599 QString errorString;
1600
1601 if (f.open(QIODevice::ReadOnly)) {
1602 QSGTextureReader texReader(&f, localFile);
1603 if (backendSupport()->hasOpenGL && texReader.isTexture()) {
1604 QQuickTextureFactory *factory = texReader.read();
1605 if (factory) {
1606 *ok = true;
1607 return new QQuickPixmapData(url, factory, factory->textureSize(), requestRegion, requestSize,
1609 } else {
1610 errorString = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
1611 if (f.fileName() != localFile)
1612 errorString += QString::fromLatin1(" (%1)").arg(f.fileName());
1613 }
1614 } else {
1615 QImage image;
1616 QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
1617 int frameCount;
1618 if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
1619 providerOptions, &appliedTransform, frame, devicePixelRatio)) {
1620 *ok = true;
1621 return new QQuickPixmapData(url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
1622 providerOptions, appliedTransform, frame, frameCount);
1623 } else if (f.fileName() != localFile) {
1624 errorString += QString::fromLatin1(" (%1)").arg(f.fileName());
1625 }
1626 }
1627 } else {
1628 errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
1629 }
1630 return new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, errorString);
1631}
1632
1633
1640
1645
1651
1653: d(nullptr)
1654{
1655 load(engine, url, options);
1656}
1657
1659: d(nullptr)
1660{
1661 load(engine, url, region, size);
1662}
1663
1670
1672{
1673 if (d) {
1674 d->release();
1675 d = nullptr;
1676 }
1677}
1678
1680{
1681 return d == nullptr;
1682}
1683
1685{
1686 return status() == Ready;
1687}
1688
1690{
1691 return status() == Error;
1692}
1693
1695{
1696 return status() == Loading;
1697}
1698
1700{
1701 if (d)
1702 return d->errorString;
1703 else
1704 return QString();
1705}
1706
1708{
1709 if (d)
1710 return d->pixmapStatus;
1711 else
1712 return Null;
1713}
1714
1716{
1717 if (d)
1718 return d->url;
1719 else
1720 return nullPixmap()->url;
1721}
1722
1724{
1725 if (d)
1726 return d->implicitSize;
1727 else
1728 return nullPixmap()->size;
1729}
1730
1732{
1733 if (d)
1734 return d->requestSize;
1735 else
1736 return nullPixmap()->size;
1737}
1738
1740{
1741 if (d)
1742 return d->requestRegion;
1743 else
1744 return nullPixmap()->region;
1745}
1746
1754
1756{
1757 if (d)
1758 return d->frameCount;
1759 return 0;
1760}
1761
1763{
1764 if (d)
1765 return d->textureFactory;
1766
1767 return nullptr;
1768}
1769
1771{
1772 if (d && d->textureFactory)
1773 return d->textureFactory->image();
1774 return QImage();
1775}
1776
1778{
1779 clear();
1780
1781 if (!p.isNull()) {
1782 if (d)
1783 d->release();
1785 }
1786}
1787
1789{
1790 if (d == other.d)
1791 return;
1792 clear();
1793
1794 if (other.d) {
1795 if (d)
1796 d->release();
1797 d = other.d;
1798 d->addref();
1799 }
1800}
1801
1803{
1804 if (d && d->textureFactory)
1805 return d->textureFactory->textureSize().width();
1806 else
1807 return 0;
1808}
1809
1811{
1812 if (d && d->textureFactory)
1813 return d->textureFactory->textureSize().height();
1814 else
1815 return 0;
1816}
1817
1819{
1820 if (d && d->textureFactory)
1821 return QRect(QPoint(), d->textureFactory->textureSize());
1822 else
1823 return QRect();
1824}
1825
1830
1831void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, QQuickPixmap::Options options)
1832{
1833 load(engine, url, QRect(), QSize(), options);
1834}
1835
1836void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize)
1837{
1839}
1840
1841void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize, QQuickPixmap::Options options)
1842{
1844}
1845
1846void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize,
1847 QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame, int frameCount,
1848 qreal devicePixelRatio)
1849{
1850 if (d) {
1851 d->release();
1852 d = nullptr;
1853 }
1854
1855 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1857
1858 QMutexLocker locker(&QQuickPixmapCache::instance()->m_cacheMutex);
1860
1861#ifdef Q_OS_WEBOS
1862 QQuickPixmap::Options orgOptions = options;
1863 // In webOS, we suppose that cache is always enabled to share image instances along its source.
1864 // So, original option(orgOptions) for cache only decides whether to store the instances when it's unreferenced.
1865 options |= QQuickPixmap::Cache;
1866#endif
1867
1868 // If Cache is disabled, the pixmap will always be loaded, even if there is an existing
1869 // cached version. Unless it's an itemgrabber url, since the cache is used to pass
1870 // the result between QQuickItemGrabResult and QQuickImage.
1871 if (url.scheme() == itemGrabberScheme) {
1872 QRect dummyRegion;
1873 QSize dummySize;
1874 if (requestSize != dummySize)
1875 qWarning() << "Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead.";
1876 const QQuickPixmapKey grabberKey = { &url, &dummyRegion, &dummySize, 0, QQuickImageProviderOptions() };
1877 iter = store->m_cache.find(grabberKey);
1878 } else if (options & QQuickPixmap::Cache)
1879 iter = store->m_cache.find(key);
1880
1881 if (iter == store->m_cache.end()) {
1882 locker.unlock();
1883 if (url.scheme() == QLatin1String("image")) {
1885 if (auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
1886 const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps);
1887 if (!threadedPixmaps && provider->imageType() == QQuickImageProvider::Pixmap) {
1888 // pixmaps can only be loaded synchronously
1889 options &= ~QQuickPixmap::Asynchronous;
1890 } else if (provider->flags() & QQuickImageProvider::ForceAsynchronousImageLoading) {
1891 options |= QQuickPixmap::Asynchronous;
1892 }
1893 }
1894 }
1895
1896 if (!(options & QQuickPixmap::Asynchronous)) {
1897 bool ok = false;
1898 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
1899 d = createPixmapDataSync(engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
1900 if (ok) {
1901 PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
1902 if (options & QQuickPixmap::Cache)
1903 d->addToCache();
1904#ifdef Q_OS_WEBOS
1905 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1906#endif
1907 return;
1908 }
1909 if (d) { // loadable, but encountered error while loading
1910 PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url));
1911 return;
1912 }
1913 }
1914
1915 if (!engine)
1916 return;
1917
1918
1919 d = new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1921 if (options & QQuickPixmap::Cache)
1922 d->addToCache();
1923#ifdef Q_OS_WEBOS
1924 d->storeToCache = orgOptions & QQuickPixmap::Cache;
1925#endif
1926
1929 d->reply = reader->getImage(d);
1930 reader->startJob(d->reply);
1932 } else {
1933 d = *iter;
1934 d->addref();
1935 qCDebug(lcImg) << "loaded from cache" << url << "frame" << frame;
1936 }
1937}
1938
1947 const QRect &requestRegion, const QSize &requestSize,
1948 const QQuickImageProviderOptions &providerOptions, int frame, int frameCount)
1949{
1950 auto oldD = d;
1951 QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions };
1954 QMutexLocker locker(&store->m_cacheMutex);
1955 iter = store->m_cache.find(key);
1956 if (iter == store->m_cache.end()) {
1957 if (!engine)
1958 return;
1959
1960 locker.unlock();
1961 d = new QQuickPixmapData(url, requestRegion, requestSize, providerOptions,
1963 d->specialDevice = device;
1964 d->addToCache();
1965
1968 d->reply = reader->getImage(d);
1969 if (oldD) {
1971 oldD->release();
1973 }
1974 reader->startJob(d->reply);
1976 } else {
1977 d = *iter;
1978 d->addref();
1979 qCDebug(lcImg) << "loaded from cache" << url << "frame" << frame << "refCount" << d->refCount;
1980 locker.unlock();
1981 if (oldD)
1982 oldD->release();
1983 }
1984}
1985
1987{
1988 if (d) {
1989 d->release();
1990 d = nullptr;
1991 }
1992}
1993
1995{
1996 if (d) {
1997 if (d->reply)
1998 QObject::disconnect(d->reply, nullptr, obj, nullptr);
1999 d->release();
2000 d = nullptr;
2001 }
2002}
2003
2004bool QQuickPixmap::isCached(const QUrl &url, const QRect &requestRegion, const QSize &requestSize,
2005 const int frame, const QQuickImageProviderOptions &options)
2006{
2009
2010 return store->m_cache.contains(key);
2011}
2012
2014{
2015 if (url.scheme() == "image"_L1)
2016 return true;
2017
2018 const QString stringUrl = url.path(QUrl::PrettyDecoded);
2019 return stringUrl.endsWith("svg"_L1)
2020 || stringUrl.endsWith("svgz"_L1)
2021 || stringUrl.endsWith("pdf"_L1);
2022}
2023
2025{
2026 if (!d || !d->reply) {
2027 qWarning("QQuickPixmap: connectFinished() called when not loading.");
2028 return false;
2029 }
2030
2031 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
2032}
2033
2035{
2036 if (!d || !d->reply) {
2037 qWarning("QQuickPixmap: connectFinished() called when not loading.");
2038 return false;
2039 }
2040
2042}
2043
2045{
2046 if (!d || !d->reply) {
2047 qWarning("QQuickPixmap: connectDownloadProgress() called when not loading.");
2048 return false;
2049 }
2050
2051 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object,
2052 method);
2053}
2054
2056{
2057 if (!d || !d->reply) {
2058 qWarning("QQuickPixmap: connectDownloadProgress() called when not loading.");
2059 return false;
2060 }
2061
2063 method);
2064}
2065
2067{
2068 if (!d || !d->textureFactory)
2069 return QColorSpace();
2070 return d->textureFactory->image().colorSpace();
2071}
2072
2074
2075#include <qquickpixmapcache.moc>
2076
2077#include "moc_qquickpixmapcache_p.cpp"
IOBluetoothDevice * device
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
The QColorSpace class provides a color space abstraction.
Definition qcolorspace.h:21
bool isValid() const noexcept
Returns true if the color space is valid.
static bool closingDown()
Returns true if the application objects are being destroyed; otherwise returns false.
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qcoreevent.h:45
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
Type type() const
Returns the event type.
Definition qcoreevent.h:304
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
static QPlatformIntegration * platformIntegration()
\inmodule QtCore
Definition qhash.h:1103
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:927
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1291
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:985
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
Key key(const T &value) const noexcept
Definition qhash.h:1034
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
QString errorString() const
Returns a human-readable description of the last device error that occurred.
The QImageReader class provides a format independent interface for reading images from files or other...
static QList< QByteArray > supportedImageFormats()
Returns the list of image formats supported by QImageReader.
\inmodule QtGui
Definition qimage.h:37
QSize size() const
Returns the size of the image, i.e.
@ Format_RGBA8888
Definition qimage.h:59
@ Format_RGB30
Definition qimage.h:63
@ Format_RGB32
Definition qimage.h:46
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_A2BGR30_Premultiplied
Definition qimage.h:62
@ Format_BGR30
Definition qimage.h:61
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
@ Format_A2RGB30_Premultiplied
Definition qimage.h:64
@ Format_RGBX8888
Definition qimage.h:58
qsizetype size() const noexcept
Definition qlist.h:397
void append(parameter_type t)
Definition qlist.h:458
static QMetaMethod fromSignal(PointerToMemberFunction signal)
\inmodule QtCore
Definition qmutex.h:313
void unlock() noexcept
Unlocks this mutex locker.
Definition qmutex.h:319
\inmodule QtCore
Definition qmutex.h:281
The QNetworkAccessManager class allows the application to send network requests and receive replies.
The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager.
virtual void close() override
Closes this device for reading.
bool isRunning() const
NetworkError error() const
Returns the error that was found during the processing of this request.
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
This signal is emitted to indicate the progress of the download part of this network request,...
void finished()
This signal is emitted when the reply has finished processing.
QUrl url() const
Returns the URL of the content downloaded or uploaded.
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:150
\inmodule QtCore
Definition qobject.h:103
int startTimer(int interval, Qt::TimerType timerType=Qt::CoarseTimer)
This is an overloaded function that will start a timer of type timerType and a timeout of interval mi...
Definition qobject.cpp:1817
Q_INVOKABLE QObject(QObject *parent=nullptr)
Constructs an object with parent object parent.
Definition qobject.cpp:936
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
Definition qobject.cpp:2658
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1389
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
void killTimer(int id)
Kills the timer with timer identifier, id.
Definition qobject.cpp:1912
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
\inmodule QtCore\reentrant
Definition qpoint.h:25
QSharedPointer< QQmlImageProviderBase > imageProvider(const QString &providerId) const
static QQmlEnginePrivate * get(QQmlEngine *e)
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
ImageType
Defines the type of image supported by this image provider.
Definition qqmlengine.h:21
The QQuickAsyncImageProvider class provides an interface for asynchronous control of QML image reques...
QImage image() const override
Returns an image version of this texture.
QQuickDefaultTextureFactory(const QImage &i)
QSGTexture * createTexture(QQuickWindow *window) const override
This function is called on the scene graph rendering thread to create a QSGTexture instance from the ...
The QQuickImageProviderOptions class provides options for QQuickImageProviderWithOptions image reques...
QColorSpace targetColorSpace() const
Returns the color space the image provider should return the image in.
AutoTransform
Whether the image provider should apply transformation metadata on read().
AutoTransform autoTransform() const
Returns whether the image provider should apply transformation metadata on read().
static QQuickImageProviderWithOptions * checkedCast(QQuickImageProvider *provider)
static QSize loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options, qreal devicePixelRatio=1.0)
Returns the recommended scaled image size for loading and storage.
The QQuickImageProvider class provides an interface for supporting pixmaps and threaded image request...
The QQuickImageResponse class provides an interface for asynchronous image loading in QQuickAsyncImag...
virtual QString errorString() const
Returns the error string for the job execution.
void finished()
Signals that the job execution has finished (be it successfully, because an error happened or because...
virtual QQuickTextureFactory * textureFactory() const =0
Returns the texture factory for the job.
static QQuickPixmapCache * instance()
void referencePixmap(QQuickPixmapData *)
void timerEvent(QTimerEvent *) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
void unreferencePixmap(QQuickPixmapData *)
QQuickPixmapReply * reply
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)
QQuickPixmapData ** prevUnreferencedPtr
QQuickTextureFactory * textureFactory
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)
QQuickPixmapData * nextUnreferenced
void release(QQuickPixmapCache *store=nullptr)
QQuickPixmap::Status pixmapStatus
QQuickPixmapData * prevUnreferenced
QQuickImageProviderOptions providerOptions
void removeFromCache(QQuickPixmapCache *store=nullptr)
static QQuickPixmapReader * instance(QQmlEngine *engine)
QQuickPixmapReader(QQmlEngine *eng)
void startJob(QQuickPixmapReply *job)
void cancel(QQuickPixmapReply *rep)
friend class ReaderThreadExecutionEnforcer
QQuickPixmapReply * getImage(QQuickPixmapData *)
static QQuickPixmapReader * existingInstance(QQmlEngine *engine)
QQuickTextureFactory * textureFactory
Event(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory)
void downloadProgress(qint64, qint64)
static int downloadProgressMethodIndex
QQuickPixmapData * data
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 *)
QColorSpace colorSpace() const
const QUrl & url() const
QQuickImageProviderOptions::AutoTransform autoTransform() const
QImage image() const
bool connectDownloadProgress(QObject *, const char *)
static bool isScalableImageFormat(const QUrl &url)
QQuickTextureFactory * textureFactory() const
const QSize & requestSize() const
static bool isCached(const QUrl &url, const QRect &requestRegion, const QSize &requestSize, const int frame, const QQuickImageProviderOptions &options)
friend class QQuickPixmapData
QString error() const
void load(QQmlEngine *, const QUrl &)
void loadImageFromDevice(QQmlEngine *engine, QIODevice *device, const QUrl &url, const QRect &requestRegion, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, int frame=0, int frameCount=1)
const QRect & requestRegion() const
const QSize & implicitSize() const
bool isLoading() const
void setImage(const QImage &)
void setPixmap(const QQuickPixmap &other)
Status status() const
static const QLatin1String itemGrabberScheme
bool connectFinished(QObject *, const char *)
static void purgeCache()
The QQuickTextureFactory class provides an interface for loading custom textures from QML....
virtual QSize textureSize() const =0
Returns the size of the texture.
virtual int textureByteCount() const =0
Returns the number of bytes of memory the texture consumes.
virtual QImage image() const
Returns an image version of this texture.
static QQuickTextureFactory * textureFactoryForImage(const QImage &image)
Returns a QQuickTextureFactory holding the given image.
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:164
static QQuickTextureFactory * createTextureFactoryFromImage(const QImage &image)
Calls into the scene graph adaptation if available and creates a texture factory.
static QList< QByteArray > supportedFileFormats()
\inmodule QtQuick
Definition qsgtexture.h:20
\inmodule QtCore
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION
Definition qthread.h:149
@ LowestPriority
Definition qthread.h:43
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:1023
void finished(QPrivateSignal)
int exec()
Definition qthread.cpp:991
void quit()
Definition qthread.cpp:1008
\inmodule QtCore
Definition qcoreevent.h:366
\inmodule QtCore
Definition qurl.h:94
QString url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2817
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2497
QString host(ComponentFormattingOptions=FullyDecoded) const
Returns the host of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2340
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1991
@ RemoveScheme
Definition qurl.h:105
@ RemoveAuthority
Definition qurl.h:109
@ PrettyDecoded
Definition qurl.h:121
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2468
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
ReaderThreadExecutionEnforcer(QQuickPixmapReader *reader)
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Q_MULTIMEDIA_EXPORT QString errorString(HRESULT hr)
static void formatQRect(QDebug &debug, const Rect &rect)
Definition qdebug_p.h:45
static void formatQSize(QDebug &debug, const Size &size)
Definition qdebug_p.h:39
@ AutoColor
Definition qnamespace.h:478
void * HANDLE
@ QueuedConnection
@ DirectConnection
Definition image.cpp:4
void all(std::vector< emscripten::val > promises, PromiseCallbacks callbacks)
Definition qstdweb.cpp:767
#define Q_FUNC_INFO
#define Q_EVENT_DISABLE_COPY(Class)
Definition qcoreevent.h:13
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
constexpr auto errorStr
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
constexpr QtPrivate::QHashMultiReturnType< T... > qHashMulti(size_t seed, const T &... args) noexcept(std::conjunction_v< QtPrivate::QNothrowHashable< T >... >)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
@ User
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint texture
GLuint start
struct _cl_event * event
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
GLdouble GLdouble t
Definition qopenglext.h:243
GLfloat GLfloat p
[1]
bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs)
#define CACHE_EXPIRE_TIME
static QString imageId(const QUrl &url)
#define Q_ASSERT_CALLED_ON_VALID_THREAD(x)
#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
static QString existingImageFileForPath(const QString &localFile)
#define PIXMAP_READER_LOCK()
static int cache_limit
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)
QDebug operator<<(QDebug debug, const QQuickPixmapKey &key)
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)
size_t qHash(const QQuickPixmapKey &key, size_t seed) noexcept
#define Q_THREAD_AFFINITY_MARKER(x)
static QStringList fromLatin1List(const QList< QByteArray > &list)
#define CACHE_REMOVAL_FRACTION
static QString imageProviderId(const QUrl &url)
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int void * arg
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define QT_CONFIG(feature)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
#define Q_OBJECT
#define slots
#define Q_SIGNALS
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QList< int > list
[14]
future cancel()
if(qFloatDistance(a, b)<(1<< 7))
[0]
settings remove("monkey")
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
sem release()
QMutex mutex
[2]
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item
QItemEditorFactory * factory
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]
QFrame frame
[0]
QNetworkReply * reply
QJSEngine engine
[0]
\inmodule QtCore
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
static Connection connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type=0, int *types=nullptr)
Definition qobject.cpp:3556
QQuickImageProviderOptions options