1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qicon.h"
6#include "qicon_p.h"
7#include "qiconengine.h"
8#include "qiconengineplugin.h"
9#include "qimagereader.h"
10#include "private/qfactoryloader_p.h"
11#include "private/qiconloader_p.h"
12#include "qpainter.h"
13#include "qfileinfo.h"
14#if QT_CONFIG(mimetype)
15#include <qmimedatabase.h>
16#include <qmimetype.h>
17#endif
18#include "qpixmapcache.h"
19#include "qvariant.h"
20#include "qcache.h"
21#include "qdebug.h"
22#include "qdir.h"
23#include "qpalette.h"
24#include "qmath.h"
25
26#include "private/qhexstring_p.h"
27#include "private/qguiapplication_p.h"
28#include "qpa/qplatformtheme.h"
29
30#ifndef QT_NO_ICON
31QT_BEGIN_NAMESPACE
32
33using namespace Qt::StringLiterals;
34
35/*!
36 \enum QIcon::Mode
37
38 This enum type describes the mode for which a pixmap is intended
39 to be used. The currently defined modes are:
40
41 \value Normal
42 Display the pixmap when the user is
43 not interacting with the icon, but the
44 functionality represented by the icon is available.
45 \value Disabled
46 Display the pixmap when the
47 functionality represented by the icon is not available.
48 \value Active
49 Display the pixmap when the
50 functionality represented by the icon is available and
51 the user is interacting with the icon, for example, moving the
52 mouse over it or clicking it.
53 \value Selected
54 Display the pixmap when the item represented by the icon is
55 selected.
56*/
57
58/*!
59 \enum QIcon::State
60
61 This enum describes the state for which a pixmap is intended to be
62 used. The \e state can be:
63
64 \value Off Display the pixmap when the widget is in an "off" state
65 \value On Display the pixmap when the widget is in an "on" state
66*/
67
68static int nextSerialNumCounter()
69{
70 Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
71 return 1 + serial.fetchAndAddRelaxed(valueToAdd: 1);
72}
73
74static void qt_cleanup_icon_cache();
75namespace {
76 struct IconCache : public QCache<QString, QIcon>
77 {
78 IconCache()
79 {
80 // ### note: won't readd if QApplication is re-created!
81 qAddPostRoutine(qt_cleanup_icon_cache);
82 }
83 };
84}
85
86Q_GLOBAL_STATIC(IconCache, qtIconCache)
87
88static void qt_cleanup_icon_cache()
89{
90 qtIconCache()->clear();
91}
92
93QIconPrivate::QIconPrivate(QIconEngine *e)
94 : engine(e), ref(1),
95 serialNum(nextSerialNumCounter()),
96 detach_no(0),
97 is_mask(false)
98{
99}
100
101void QIconPrivate::clearIconCache()
102{
103 qt_cleanup_icon_cache();
104}
105
106/*! \internal
107 Computes the displayDevicePixelRatio for a pixmap.
108
109 If displayDevicePixelRatio is 1.0 the reurned value is 1.0, always.
110
111 For a displayDevicePixelRatio of 2.0 the returned value will be between
112 1.0 and 2.0, depending on requestedSize and actualsize:
113 * If actualsize < requestedSize : 1.0 (not enough pixels for a normal-dpi pixmap)
114 * If actualsize == requestedSize * 2.0 : 2.0 (enough pixels for a high-dpi pixmap)
115 * else : a scaled value between 1.0 and 2.0. (pixel count is between normal-dpi and high-dpi)
116*/
117qreal QIconPrivate::pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize)
118{
119 QSize targetSize = requestedSize * displayDevicePixelRatio;
120 if ((actualSize.width() == targetSize.width() && actualSize.height() <= targetSize.height()) ||
121 (actualSize.width() <= targetSize.width() && actualSize.height() == targetSize.height())) {
122 // Correctly scaled for dpr, just having different aspect ratio
123 return displayDevicePixelRatio;
124 }
125 qreal scale = 0.5 * (qreal(actualSize.width()) / qreal(targetSize.width()) +
126 qreal(actualSize.height() / qreal(targetSize.height())));
127 return qMax(a: qreal(1.0), b: displayDevicePixelRatio *scale);
128}
129
130QPixmapIconEngine::QPixmapIconEngine()
131{
132}
133
134QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other)
135 : QIconEngine(other), pixmaps(other.pixmaps)
136{
137}
138
139QPixmapIconEngine::~QPixmapIconEngine()
140{
141}
142
143void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
144{
145 auto paintDevice = painter->device();
146 qreal dpr = paintDevice ? paintDevice->devicePixelRatio() : qApp->devicePixelRatio();
147 const QSize pixmapSize = rect.size() * dpr;
148 QPixmap px = scaledPixmap(size: pixmapSize, mode, state, scale: dpr);
149 painter->drawPixmap(r: rect, pm: px);
150}
151
152static inline int area(const QSize &s) { return s.width() * s.height(); }
153
154// Returns the smallest of the two that is still larger than or equal to size.
155// Pixmaps at the correct scale are preferred, pixmaps at lower scale are
156// used as fallbacks. We assume that the pixmap set is complete, in the sense
157// that no 2x pixmap is going to be a better match than a 3x pixmap for the the
158// target scale of 3 (It's OK if 3x pixmaps are missing - we'll fall back to
159// the 2x pixmaps then.)
160static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
161{
162
163 // scale: we can only differentiate on scale if the scale differs
164 if (pa->scale != pb->scale) {
165
166 // Score the pixmaps: 0 is an exact scale match, positive
167 // scores have more detail than requested, negative scores
168 // have less detail than requested.
169 qreal ascore = pa->scale - scale;
170 qreal bscore = pb->scale - scale;
171
172 // Take the one closest to 0
173 return (qAbs(t: ascore) < qAbs(t: bscore)) ? pa : pb;
174 }
175
176 int s = area(s: size);
177 if (pa->size == QSize() && pa->pixmap.isNull()) {
178 pa->pixmap = QPixmap(pa->fileName);
179 pa->size = pa->pixmap.size();
180 }
181 int a = area(s: pa->size);
182 if (pb->size == QSize() && pb->pixmap.isNull()) {
183 pb->pixmap = QPixmap(pb->fileName);
184 pb->size = pb->pixmap.size();
185 }
186 int b = area(s: pb->size);
187 int res = a;
188 if (qMin(a,b) >= s)
189 res = qMin(a,b);
190 else
191 res = qMax(a,b);
192 if (res == a)
193 return pa;
194 return pb;
195}
196
197QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state)
198{
199 QPixmapIconEngineEntry *pe = nullptr;
200 for (int i = 0; i < pixmaps.size(); ++i)
201 if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
202 if (pe)
203 pe = bestSizeScaleMatch(size, scale, pa: &pixmaps[i], pb: pe);
204 else
205 pe = &pixmaps[i];
206 }
207 return pe;
208}
209
210
211QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
212{
213 QPixmapIconEngineEntry *pe = tryMatch(size, scale, mode, state);
214 while (!pe){
215 QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On;
216 if (mode == QIcon::Disabled || mode == QIcon::Selected) {
217 QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled;
218 if ((pe = tryMatch(size, scale, mode: QIcon::Normal, state)))
219 break;
220 if ((pe = tryMatch(size, scale, mode: QIcon::Active, state)))
221 break;
222 if ((pe = tryMatch(size, scale, mode, state: oppositeState)))
223 break;
224 if ((pe = tryMatch(size, scale, mode: QIcon::Normal, state: oppositeState)))
225 break;
226 if ((pe = tryMatch(size, scale, mode: QIcon::Active, state: oppositeState)))
227 break;
228 if ((pe = tryMatch(size, scale, mode: oppositeMode, state)))
229 break;
230 if ((pe = tryMatch(size, scale, mode: oppositeMode, state: oppositeState)))
231 break;
232 } else {
233 QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal;
234 if ((pe = tryMatch(size, scale, mode: oppositeMode, state)))
235 break;
236 if ((pe = tryMatch(size, scale, mode, state: oppositeState)))
237 break;
238 if ((pe = tryMatch(size, scale, mode: oppositeMode, state: oppositeState)))
239 break;
240 if ((pe = tryMatch(size, scale, mode: QIcon::Disabled, state)))
241 break;
242 if ((pe = tryMatch(size, scale, mode: QIcon::Selected, state)))
243 break;
244 if ((pe = tryMatch(size, scale, mode: QIcon::Disabled, state: oppositeState)))
245 break;
246 if ((pe = tryMatch(size, scale, mode: QIcon::Selected, state: oppositeState)))
247 break;
248 }
249
250 if (!pe)
251 return pe;
252 }
253
254 if (sizeOnly ? (pe->size.isNull() || !pe->size.isValid()) : pe->pixmap.isNull()) {
255 pe->pixmap = QPixmap(pe->fileName);
256 if (!pe->pixmap.isNull())
257 pe->size = pe->pixmap.size();
258 }
259
260 return pe;
261}
262
263QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
264{
265 return scaledPixmap(size, mode, state, scale: 1.0);
266}
267
268QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
269{
270
271 QPixmap pm;
272 QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, sizeOnly: false);
273 if (pe)
274 pm = pe->pixmap;
275
276 if (pm.isNull()) {
277 int idx = pixmaps.size();
278 while (--idx >= 0) {
279 if (pe == &pixmaps.at(i: idx)) {
280 pixmaps.remove(i: idx);
281 break;
282 }
283 }
284 if (pixmaps.isEmpty())
285 return pm;
286 else
287 return pixmap(size, mode, state);
288 }
289
290 QSize actualSize = pm.size();
291 if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
292 actualSize.scale(s: size, mode: Qt::KeepAspectRatio);
293
294 QString key = "qt_"_L1
295 % HexString<quint64>(pm.cacheKey())
296 % HexString<uint>(pe ? pe->mode : QIcon::Normal)
297 % HexString<quint64>(QGuiApplication::palette().cacheKey())
298 % HexString<uint>(actualSize.width())
299 % HexString<uint>(actualSize.height());
300
301 if (mode == QIcon::Active) {
302 if (QPixmapCache::find(key: key % HexString<uint>(mode), pixmap: &pm))
303 return pm; // horray
304 if (QPixmapCache::find(key: key % HexString<uint>(QIcon::Normal), pixmap: &pm)) {
305 QPixmap active = pm;
306 if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
307 active = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(o: guiApp))->applyQIconStyleHelper(QIcon::Active, basePixmap: pm);
308 if (pm.cacheKey() == active.cacheKey())
309 return pm;
310 }
311 }
312
313 if (!QPixmapCache::find(key: key % HexString<uint>(mode), pixmap: &pm)) {
314 if (pm.size() != actualSize)
315 pm = pm.scaled(s: actualSize, aspectMode: Qt::IgnoreAspectRatio, mode: Qt::SmoothTransformation);
316 if (pe->mode != mode && mode != QIcon::Normal) {
317 QPixmap generated = pm;
318 if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
319 generated = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(o: guiApp))->applyQIconStyleHelper(mode, basePixmap: pm);
320 if (!generated.isNull())
321 pm = generated;
322 }
323 QPixmapCache::insert(key: key % HexString<uint>(mode), pixmap: pm);
324 }
325 return pm;
326}
327
328QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
329{
330 QSize actualSize;
331
332 // The returned actual size is the size in device independent pixels,
333 // so we limit the search to scale 1 and assume that e.g. @2x versions
334 // does not proviode extra actual sizes not also provided by the 1x versions.
335 qreal scale = 1;
336
337 if (QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, sizeOnly: true))
338 actualSize = pe->size;
339
340 if (actualSize.isNull())
341 return actualSize;
342
343 if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
344 actualSize.scale(s: size, mode: Qt::KeepAspectRatio);
345 return actualSize;
346}
347
348QList<QSize> QPixmapIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
349{
350 QList<QSize> sizes;
351 for (int i = 0; i < pixmaps.size(); ++i) {
352 QPixmapIconEngineEntry &pe = pixmaps[i];
353 if (pe.size == QSize() && pe.pixmap.isNull()) {
354 pe.pixmap = QPixmap(pe.fileName);
355 pe.size = pe.pixmap.size();
356 }
357 if (pe.mode == mode && pe.state == state && !pe.size.isEmpty())
358 sizes.push_back(t: pe.size);
359 }
360 return sizes;
361}
362
363void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
364{
365 if (!pixmap.isNull()) {
366 QPixmapIconEngineEntry *pe = tryMatch(size: pixmap.size(), scale: pixmap.devicePixelRatio(), mode, state);
367 if (pe && pe->size == pixmap.size() && pe->scale == pixmap.devicePixelRatio()) {
368 pe->pixmap = pixmap;
369 pe->fileName.clear();
370 } else {
371 pixmaps += QPixmapIconEngineEntry(pixmap, mode, state);
372 }
373 }
374}
375
376// Read out original image depth as set by ICOReader
377static inline int origIcoDepth(const QImage &image)
378{
379 const QString s = image.text(QStringLiteral("_q_icoOrigDepth"));
380 return s.isEmpty() ? 32 : s.toInt();
381}
382
383static inline int findBySize(const QList<QImage> &images, const QSize &size)
384{
385 for (int i = 0; i < images.size(); ++i) {
386 if (images.at(i).size() == size)
387 return i;
388 }
389 return -1;
390}
391
392// Convenience class providing a bool read() function.
393namespace {
394class ImageReader
395{
396public:
397 ImageReader(const QString &fileName) : m_reader(fileName), m_atEnd(false) {}
398
399 QByteArray format() const { return m_reader.format(); }
400
401 bool read(QImage *image)
402 {
403 if (m_atEnd)
404 return false;
405 *image = m_reader.read();
406 if (!image->size().isValid()) {
407 m_atEnd = true;
408 return false;
409 }
410 m_atEnd = !m_reader.jumpToNextImage();
411 return true;
412 }
413
414private:
415 QImageReader m_reader;
416 bool m_atEnd;
417};
418} // namespace
419
420void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
421{
422 if (fileName.isEmpty())
423 return;
424 const QString abs = fileName.startsWith(c: u':') ? fileName : QFileInfo(fileName).absoluteFilePath();
425 const bool ignoreSize = !size.isValid();
426 ImageReader imageReader(abs);
427 const QByteArray format = imageReader.format();
428 if (format.isEmpty()) // Device failed to open or unsupported format.
429 return;
430 QImage image;
431 if (format != "ico") {
432 if (ignoreSize) { // No size specified: Add all images.
433 while (imageReader.read(image: &image))
434 pixmaps += QPixmapIconEngineEntry(abs, image, mode, state);
435 } else {
436 // Try to match size. If that fails, add a placeholder with the filename and empty pixmap for the size.
437 while (imageReader.read(image: &image) && image.size() != size) {}
438 pixmaps += image.size() == size ?
439 QPixmapIconEngineEntry(abs, image, mode, state) : QPixmapIconEngineEntry(abs, size, mode, state);
440 }
441 return;
442 }
443 // Special case for reading Windows ".ico" files. Historically (QTBUG-39287),
444 // these files may contain low-resolution images. As this information is lost,
445 // ICOReader sets the original format as an image text key value. Read all matching
446 // images into a list trying to find the highest quality per size.
447 QList<QImage> icoImages;
448 while (imageReader.read(image: &image)) {
449 if (ignoreSize || image.size() == size) {
450 const int position = findBySize(images: icoImages, size: image.size());
451 if (position >= 0) { // Higher quality available? -> replace.
452 if (origIcoDepth(image) > origIcoDepth(image: icoImages.at(i: position)))
453 icoImages[position] = image;
454 } else {
455 icoImages.append(t: image);
456 }
457 }
458 }
459 for (const QImage &i : std::as_const(t&: icoImages))
460 pixmaps += QPixmapIconEngineEntry(abs, i, mode, state);
461 if (icoImages.isEmpty() && !ignoreSize) // Add placeholder with the filename and empty pixmap for the size.
462 pixmaps += QPixmapIconEngineEntry(abs, size, mode, state);
463}
464
465QString QPixmapIconEngine::key() const
466{
467 return "QPixmapIconEngine"_L1;
468}
469
470QIconEngine *QPixmapIconEngine::clone() const
471{
472 return new QPixmapIconEngine(*this);
473}
474
475bool QPixmapIconEngine::read(QDataStream &in)
476{
477 int num_entries;
478 QPixmap pm;
479 QString fileName;
480 QSize sz;
481 uint mode;
482 uint state;
483
484 in >> num_entries;
485 for (int i=0; i < num_entries; ++i) {
486 if (in.atEnd()) {
487 pixmaps.clear();
488 return false;
489 }
490 in >> pm;
491 in >> fileName;
492 in >> sz;
493 in >> mode;
494 in >> state;
495 if (pm.isNull()) {
496 addFile(fileName, size: sz, mode: QIcon::Mode(mode), state: QIcon::State(state));
497 } else {
498 QPixmapIconEngineEntry pe(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
499 pe.pixmap = pm;
500 pixmaps += pe;
501 }
502 }
503 return true;
504}
505
506bool QPixmapIconEngine::write(QDataStream &out) const
507{
508 int num_entries = pixmaps.size();
509 out << num_entries;
510 for (int i=0; i < num_entries; ++i) {
511 if (pixmaps.at(i).pixmap.isNull())
512 out << QPixmap(pixmaps.at(i).fileName);
513 else
514 out << pixmaps.at(i).pixmap;
515 out << pixmaps.at(i).fileName;
516 out << pixmaps.at(i).size;
517 out << (uint) pixmaps.at(i).mode;
518 out << (uint) pixmaps.at(i).state;
519 }
520 return true;
521}
522
523Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, iceLoader,
524 (QIconEngineFactoryInterface_iid, "/iconengines"_L1, Qt::CaseInsensitive))
525
526QFactoryLoader *qt_iconEngineFactoryLoader()
527{
528 return iceLoader();
529}
530
531
532/*!
533 \class QIcon
534
535 \brief The QIcon class provides scalable icons in different modes
536 and states.
537
538 \ingroup painting
539 \ingroup shared
540 \inmodule QtGui
541
542 A QIcon can generate smaller, larger, active, and disabled pixmaps
543 from the set of pixmaps it is given. Such pixmaps are used by Qt
544 widgets to show an icon representing a particular action.
545
546 The simplest use of QIcon is to create one from a QPixmap file or
547 resource, and then use it, allowing Qt to work out all the required
548 icon styles and sizes. For example:
549
550 \snippet code/src_gui_image_qicon.cpp 0
551
552 To undo a QIcon, simply set a null icon in its place:
553
554 \snippet code/src_gui_image_qicon.cpp 1
555
556 Use the QImageReader::supportedImageFormats() and
557 QImageWriter::supportedImageFormats() functions to retrieve a
558 complete list of the supported file formats.
559
560 When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
561 pixmap for this given size, mode and state has been added with
562 addFile() or addPixmap(), then QIcon will generate one on the
563 fly. This pixmap generation happens in a QIconEngine. The default
564 engine scales pixmaps down if required, but never up, and it uses
565 the current style to calculate a disabled appearance. By using
566 custom icon engines, you can customize every aspect of generated
567 icons. With QIconEnginePlugin it is possible to register different
568 icon engines for different file suffixes, making it possible for
569 third parties to provide additional icon engines to those included
570 with Qt.
571
572 \note Since Qt 4.2, an icon engine that supports SVG is included.
573
574 \section1 Making Classes that Use QIcon
575
576 If you write your own widgets that have an option to set a small
577 pixmap, consider allowing a QIcon to be set for that pixmap. The
578 Qt class QToolButton is an example of such a widget.
579
580 Provide a method to set a QIcon, and when you draw the icon, choose
581 whichever pixmap is appropriate for the current state of your widget.
582 For example:
583 \snippet code/src_gui_image_qicon.cpp 2
584
585 You might also make use of the \c Active mode, perhaps making your
586 widget \c Active when the mouse is over the widget (see \l
587 QWidget::enterEvent()), while the mouse is pressed pending the
588 release that will activate the function, or when it is the currently
589 selected item. If the widget can be toggled, the "On" mode might be
590 used to draw a different icon.
591
592 \image icon.png QIcon
593
594 \note QIcon needs a QGuiApplication instance before the icon is created.
595
596 \section1 High DPI Icons
597
598 There are two ways that QIcon supports \l {High DPI}{high DPI}
599 icons: via \l addFile() and \l fromTheme().
600
601 \l addFile() is useful if you have your own custom directory structure and do
602 not need to use the \l {Icon Theme Specification}{freedesktop.org Icon Theme
603 Specification}. Icons created via this approach use Qt's \l {High Resolution
604 Versions of Images}{"@nx" high DPI syntax}.
605
606 Using \l fromTheme() is necessary if you plan on following the Icon Theme
607 Specification. To make QIcon use the high DPI version of an image, add an
608 additional entry to the appropriate \c index.theme file:
609
610 \badcode
611 [Icon Theme]
612 Name=Test
613 Comment=Test Theme
614
615 Directories=32x32/actions,32x32@2/actions
616
617 [32x32/actions]
618 Size=32
619 Context=Actions
620 Type=Fixed
621
622 # High DPI version of the entry above.
623 [32x32@2/actions]
624 Size=32
625 Scale=2
626 Type=Fixed
627 \endcode
628
629 Your icon theme directory would then look something like this:
630
631 \badcode
632 ├── 32x32
633 │ └── actions
634 │ └── appointment-new.png
635 ├── 32x32@2
636 │ └── actions
637 │ └── appointment-new.png
638 └── index.theme
639 \endcode
640*/
641
642
643/*!
644 Constructs a null icon.
645*/
646QIcon::QIcon() noexcept
647 : d(nullptr)
648{
649}
650
651/*!
652 Constructs an icon from a \a pixmap.
653 */
654QIcon::QIcon(const QPixmap &pixmap)
655 :d(nullptr)
656{
657 addPixmap(pixmap);
658}
659
660/*!
661 Constructs a copy of \a other. This is very fast.
662*/
663QIcon::QIcon(const QIcon &other)
664 :d(other.d)
665{
666 if (d)
667 d->ref.ref();
668}
669
670/*!
671 \fn QIcon::QIcon(QIcon &&other)
672
673 Move-constructs a QIcon instance, making it point to the same object
674 that \a other was pointing to.
675*/
676
677/*!
678 Constructs an icon from the file with the given \a fileName. The
679 file will be loaded on demand.
680
681 If \a fileName contains a relative path (e.g. the filename only)
682 the relevant file must be found relative to the runtime working
683 directory.
684
685 The file name can refer to an actual file on disk or to
686 one of the application's embedded resources. See the
687 \l{resources.html}{Resource System} overview for details on how to
688 embed images and other resource files in the application's
689 executable.
690
691 Use the QImageReader::supportedImageFormats() and
692 QImageWriter::supportedImageFormats() functions to retrieve a
693 complete list of the supported file formats.
694*/
695QIcon::QIcon(const QString &fileName)
696 : d(nullptr)
697{
698 addFile(fileName);
699}
700
701
702/*!
703 Creates an icon with a specific icon \a engine. The icon takes
704 ownership of the engine.
705*/
706QIcon::QIcon(QIconEngine *engine)
707 :d(new QIconPrivate(engine))
708{
709}
710
711/*!
712 Destroys the icon.
713*/
714QIcon::~QIcon()
715{
716 if (d && !d->ref.deref())
717 delete d;
718}
719
720/*!
721 Assigns the \a other icon to this icon and returns a reference to
722 this icon.
723*/
724QIcon &QIcon::operator=(const QIcon &other)
725{
726 if (other.d)
727 other.d->ref.ref();
728 if (d && !d->ref.deref())
729 delete d;
730 d = other.d;
731 return *this;
732}
733
734/*!
735 \fn QIcon &QIcon::operator=(QIcon &&other)
736
737 Move-assigns \a other to this QIcon instance.
738
739 \since 5.2
740*/
741
742/*!
743 \fn void QIcon::swap(QIcon &other)
744 \since 4.8
745
746 Swaps icon \a other with this icon. This operation is very
747 fast and never fails.
748*/
749
750/*!
751 Returns the icon as a QVariant.
752*/
753QIcon::operator QVariant() const
754{
755 return QVariant::fromValue(value: *this);
756}
757
758/*!
759 Returns a number that identifies the contents of this QIcon
760 object. Distinct QIcon objects can have the same key if
761 they refer to the same contents.
762 \since 4.3
763
764 The cacheKey() will change when the icon is altered via
765 addPixmap() or addFile().
766
767 Cache keys are mostly useful in conjunction with caching.
768
769 \sa QPixmap::cacheKey()
770*/
771qint64 QIcon::cacheKey() const
772{
773 if (!d)
774 return 0;
775 return (((qint64) d->serialNum) << 32) | ((qint64) (d->detach_no));
776}
777
778/*!
779 Returns a pixmap with the requested \a size, \a mode, and \a
780 state, generating one if necessary. The pixmap might be smaller than
781 requested, but never larger, unless the device-pixel ratio of the returned
782 pixmap is larger than 1.
783
784 \sa actualSize(), paint()
785*/
786QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const
787{
788 if (!d)
789 return QPixmap();
790 const qreal dpr = -1; // don't know target dpr
791 return pixmap(size, devicePixelRatio: dpr, mode, state);
792}
793
794/*!
795 \fn QPixmap QIcon::pixmap(int w, int h, Mode mode = Normal, State state = Off) const
796
797 \overload
798
799 Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than
800 requested, but never larger, unless the device-pixel ratio of the returned
801 pixmap is larger than 1.
802*/
803
804/*!
805 \fn QPixmap QIcon::pixmap(int extent, Mode mode = Normal, State state = Off) const
806
807 \overload
808
809 Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller
810 than requested, but never larger, unless the device-pixel ratio of the returned
811 pixmap is larger than 1.
812*/
813
814/*!
815 \overload
816 \since 6.0
817
818 Returns a pixmap with the requested \a size, \a devicePixelRatio, \a mode, and \a
819 state, generating one if necessary.
820
821 \sa actualSize(), paint()
822*/
823QPixmap QIcon::pixmap(const QSize &size, qreal devicePixelRatio, Mode mode, State state) const
824{
825 if (!d)
826 return QPixmap();
827
828 // Use the global devicePixelRatio if the caller does not know the target dpr
829 if (devicePixelRatio == -1)
830 devicePixelRatio = qApp->devicePixelRatio();
831
832 // Handle the simple normal-dpi case
833 if (!(devicePixelRatio > 1.0)) {
834 QPixmap pixmap = d->engine->pixmap(size, mode, state);
835 pixmap.setDevicePixelRatio(1.0);
836 return pixmap;
837 }
838
839 // Try get a pixmap that is big enough to be displayed at device pixel resolution.
840 QPixmap pixmap = d->engine->scaledPixmap(size: size * devicePixelRatio, mode, state, scale: devicePixelRatio);
841 pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(displayDevicePixelRatio: devicePixelRatio, requestedSize: size, actualSize: pixmap.size()));
842 return pixmap;
843}
844
845#if QT_DEPRECATED_SINCE(6, 0)
846/*!
847 \since 5.1
848 \deprecated [6.0] Use pixmap(size, devicePixelRatio) instead.
849
850 Returns a pixmap with the requested \a window \a size, \a mode, and \a
851 state, generating one if necessary.
852
853 The pixmap can be smaller than the requested size. If \a window is on
854 a high-dpi display the pixmap can be larger. In that case it will have
855 a devicePixelRatio larger than 1.
856
857 \sa actualSize(), paint()
858*/
859
860QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state) const
861{
862 if (!d)
863 return QPixmap();
864
865 qreal devicePixelRatio = window ? window->devicePixelRatio() : qApp->devicePixelRatio();
866 return pixmap(size, devicePixelRatio, mode, state);
867}
868#endif
869
870
871/*! Returns the actual size of the icon for the requested \a size, \a
872 mode, and \a state. The result might be smaller than requested, but
873 never larger. The returned size is in device-independent pixels (This
874 is relevant for high-dpi pixmaps.)
875
876 \sa pixmap(), paint()
877*/
878QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const
879{
880 if (!d)
881 return QSize();
882
883 const qreal devicePixelRatio = qApp->devicePixelRatio();
884
885 // Handle the simple normal-dpi case:
886 if (!(devicePixelRatio > 1.0))
887 return d->engine->actualSize(size, mode, state);
888
889 const QSize actualSize = d->engine->actualSize(size: size * devicePixelRatio, mode, state);
890 return actualSize / d->pixmapDevicePixelRatio(displayDevicePixelRatio: devicePixelRatio, requestedSize: size, actualSize);
891}
892
893#if QT_DEPRECATED_SINCE(6, 0)
894/*!
895 \since 5.1
896 \deprecated [6.0] Use actualSize(size) instead.
897
898 Returns the actual size of the icon for the requested \a window \a size, \a
899 mode, and \a state.
900
901 The pixmap can be smaller than the requested size. The returned size
902 is in device-independent pixels (This is relevant for high-dpi pixmaps.)
903
904 \sa actualSize(), pixmap(), paint()
905*/
906
907QSize QIcon::actualSize(QWindow *window, const QSize &size, Mode mode, State state) const
908{
909 if (!d)
910 return QSize();
911
912 qreal devicePixelRatio = window ? window->devicePixelRatio() : qApp->devicePixelRatio();
913
914 // Handle the simple normal-dpi case:
915 if (!(devicePixelRatio > 1.0))
916 return d->engine->actualSize(size, mode, state);
917
918 QSize actualSize = d->engine->actualSize(size: size * devicePixelRatio, mode, state);
919 return actualSize / d->pixmapDevicePixelRatio(displayDevicePixelRatio: devicePixelRatio, requestedSize: size, actualSize);
920}
921#endif
922
923/*!
924 Uses the \a painter to paint the icon with specified \a alignment,
925 required \a mode, and \a state into the rectangle \a rect.
926
927 \sa actualSize(), pixmap()
928*/
929void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const
930{
931 if (!d || !painter)
932 return;
933
934 // Copy of QStyle::alignedRect
935 const QSize size = d->engine->actualSize(size: rect.size(), mode, state);
936 alignment = QGuiApplicationPrivate::visualAlignment(direction: painter->layoutDirection(), alignment);
937 int x = rect.x();
938 int y = rect.y();
939 int w = size.width();
940 int h = size.height();
941 if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
942 y += rect.size().height()/2 - h/2;
943 else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
944 y += rect.size().height() - h;
945 if ((alignment & Qt::AlignRight) == Qt::AlignRight)
946 x += rect.size().width() - w;
947 else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
948 x += rect.size().width()/2 - w/2;
949 QRect alignedRect(x, y, w, h);
950
951 d->engine->paint(painter, rect: alignedRect, mode, state);
952}
953
954/*!
955 \fn void QIcon::paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment,
956 Mode mode, State state) const
957
958 \overload
959
960 Paints the icon into the rectangle QRect(\a x, \a y, \a w, \a h).
961*/
962
963/*!
964 Returns \c true if the icon is empty; otherwise returns \c false.
965
966 An icon is empty if it has neither a pixmap nor a filename.
967
968 Note: Even a non-null icon might not be able to create valid
969 pixmaps, eg. if the file does not exist or cannot be read.
970*/
971bool QIcon::isNull() const
972{
973 return !d || d->engine->isNull();
974}
975
976/*!\internal
977 */
978bool QIcon::isDetached() const
979{
980 return !d || d->ref.loadRelaxed() == 1;
981}
982
983/*! \internal
984 */
985void QIcon::detach()
986{
987 if (d) {
988 if (d->engine->isNull()) {
989 if (!d->ref.deref())
990 delete d;
991 d = nullptr;
992 return;
993 } else if (d->ref.loadRelaxed() != 1) {
994 QIconPrivate *x = new QIconPrivate(d->engine->clone());
995 if (!d->ref.deref())
996 delete d;
997 d = x;
998 }
999 ++d->detach_no;
1000 }
1001}
1002
1003/*!
1004 Adds \a pixmap to the icon, as a specialization for \a mode and
1005 \a state.
1006
1007 Custom icon engines are free to ignore additionally added
1008 pixmaps.
1009
1010 \sa addFile()
1011*/
1012void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
1013{
1014 if (pixmap.isNull())
1015 return;
1016 detach();
1017 if (!d)
1018 d = new QIconPrivate(new QPixmapIconEngine);
1019 d->engine->addPixmap(pixmap, mode, state);
1020}
1021
1022static QIconEngine *iconEngineFromSuffix(const QString &fileName, const QString &suffix)
1023{
1024 if (!suffix.isEmpty()) {
1025 const int index = iceLoader()->indexOf(needle: suffix);
1026 if (index != -1) {
1027 if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(object: iceLoader()->instance(index))) {
1028 return factory->create(filename: fileName);
1029 }
1030 }
1031 }
1032 return nullptr;
1033}
1034
1035/*! Adds an image from the file with the given \a fileName to the
1036 icon, as a specialization for \a size, \a mode and \a state. The
1037 file will be loaded on demand. Note: custom icon engines are free
1038 to ignore additionally added pixmaps.
1039
1040 If \a fileName contains a relative path (e.g. the filename only)
1041 the relevant file must be found relative to the runtime working
1042 directory.
1043
1044 The file name can refer to an actual file on disk or to
1045 one of the application's embedded resources. See the
1046 \l{resources.html}{Resource System} overview for details on how to
1047 embed images and other resource files in the application's
1048 executable.
1049
1050 Use the QImageReader::supportedImageFormats() and
1051 QImageWriter::supportedImageFormats() functions to retrieve a
1052 complete list of the supported file formats.
1053
1054 If a high resolution version of the image exists (identified by
1055 the suffix \c @2x on the base name), it is automatically loaded
1056 and added with the \e{device pixel ratio} set to a value of 2.
1057 This can be disabled by setting the environment variable
1058 \c QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING (see QImageReader).
1059
1060 \note When you add a non-empty filename to a QIcon, the icon becomes
1061 non-null, even if the file doesn't exist or points to a corrupt file.
1062
1063 \sa addPixmap(), QPixmap::devicePixelRatio()
1064 */
1065void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state)
1066{
1067 if (fileName.isEmpty())
1068 return;
1069 detach();
1070 if (!d) {
1071
1072 QFileInfo info(fileName);
1073 QString suffix = info.suffix();
1074#if QT_CONFIG(mimetype)
1075 if (suffix.isEmpty())
1076 suffix = QMimeDatabase().mimeTypeForFile(fileInfo: info).preferredSuffix(); // determination from contents
1077#endif // mimetype
1078 QIconEngine *engine = iconEngineFromSuffix(fileName, suffix);
1079 d = new QIconPrivate(engine ? engine : new QPixmapIconEngine);
1080 }
1081
1082 d->engine->addFile(fileName, size, mode, state);
1083
1084 // Check if a "@Nx" file exists and add it.
1085 QString atNxFileName = qt_findAtNxFile(baseFileName: fileName, qApp->devicePixelRatio());
1086 if (atNxFileName != fileName)
1087 d->engine->addFile(fileName: atNxFileName, size, mode, state);
1088}
1089
1090/*!
1091 \since 4.5
1092
1093 Returns a list of available icon sizes for the specified \a mode and
1094 \a state.
1095*/
1096QList<QSize> QIcon::availableSizes(Mode mode, State state) const
1097{
1098 if (!d || !d->engine)
1099 return QList<QSize>();
1100 return d->engine->availableSizes(mode, state);
1101}
1102
1103/*!
1104 \since 4.7
1105
1106 Returns the name used to create the icon, if available.
1107
1108 Depending on the way the icon was created, it may have an associated
1109 name. This is the case for icons created with fromTheme().
1110
1111 \sa fromTheme(), QIconEngine::iconName()
1112*/
1113QString QIcon::name() const
1114{
1115 if (!d || !d->engine)
1116 return QString();
1117 return d->engine->iconName();
1118}
1119
1120/*!
1121 \since 4.6
1122
1123 Sets the search paths for icon themes to \a paths.
1124
1125 The content of \a paths should follow the theme format
1126 documented by setThemeName().
1127
1128 \sa themeSearchPaths(), fromTheme(), setThemeName()
1129*/
1130void QIcon::setThemeSearchPaths(const QStringList &paths)
1131{
1132 QIconLoader::instance()->setThemeSearchPath(paths);
1133}
1134
1135/*!
1136 \since 4.6
1137
1138 Returns the search paths for icon themes.
1139
1140 The default search paths will be defined by the platform.
1141 All platforms will also have the resource directory \c{:\icons} as a fallback.
1142
1143 \sa setThemeSearchPaths(), fromTheme(), setThemeName()
1144*/
1145QStringList QIcon::themeSearchPaths()
1146{
1147 return QIconLoader::instance()->themeSearchPaths();
1148}
1149
1150/*!
1151 \since 5.11
1152
1153 Returns the fallback search paths for icons.
1154
1155 The fallback search paths are consulted for standalone
1156 icon files if the \l{themeName()}{current icon theme}
1157 or \l{fallbackIconTheme()}{fallback icon theme} do
1158 not provide results for an icon lookup.
1159
1160 If not set, the fallback search paths will be defined
1161 by the platform.
1162
1163 \sa setFallbackSearchPaths(), themeSearchPaths()
1164*/
1165QStringList QIcon::fallbackSearchPaths()
1166{
1167 return QIconLoader::instance()->fallbackSearchPaths();
1168}
1169
1170/*!
1171 \since 5.11
1172
1173 Sets the fallback search paths for icons to \a paths.
1174
1175 The fallback search paths are consulted for standalone
1176 icon files if the \l{themeName()}{current icon theme}
1177 or \l{fallbackIconTheme()}{fallback icon theme} do
1178 not provide results for an icon lookup.
1179
1180 For example:
1181
1182 \snippet code/src_gui_image_qicon.cpp 5
1183
1184 \sa fallbackSearchPaths(), setThemeSearchPaths()
1185*/
1186void QIcon::setFallbackSearchPaths(const QStringList &paths)
1187{
1188 QIconLoader::instance()->setFallbackSearchPaths(paths);
1189}
1190
1191/*!
1192 \since 4.6
1193
1194 Sets the current icon theme to \a name.
1195
1196 The theme will be will be looked up in themeSearchPaths().
1197
1198 At the moment the only supported icon theme format is the
1199 \l{Freedesktop Icon Theme Specification}. The \a name should
1200 correspond to a directory name in the themeSearchPath()
1201 containing an \c index.theme file describing its contents.
1202
1203 \externalpage http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
1204 \title Freedesktop Icon Theme Specification
1205
1206 \sa themeSearchPaths(), themeName()
1207*/
1208void QIcon::setThemeName(const QString &name)
1209{
1210 QIconLoader::instance()->setThemeName(name);
1211}
1212
1213/*!
1214 \since 4.6
1215
1216 Returns the name of the current icon theme.
1217
1218 If not set, the current icon theme will be defined by the
1219 platform.
1220
1221 \note Platform icon themes are only implemented on
1222 \l{Freedesktop} based systems at the moment, and the
1223 icon theme depends on your desktop settings.
1224
1225 \externalpage https://www.freedesktop.org/
1226 \title Freedesktop
1227
1228 \sa setThemeName(), themeSearchPaths(), fromTheme(),
1229 hasThemeIcon()
1230*/
1231QString QIcon::themeName()
1232{
1233 return QIconLoader::instance()->themeName();
1234}
1235
1236/*!
1237 \since 5.12
1238
1239 Returns the name of the fallback icon theme.
1240
1241 If not set, the fallback icon theme will be defined by the
1242 platform.
1243
1244 \note Platform fallback icon themes are only implemented on
1245 \l{Freedesktop} based systems at the moment, and the
1246 icon theme depends on your desktop settings.
1247
1248 \sa setFallbackThemeName(), themeName()
1249*/
1250QString QIcon::fallbackThemeName()
1251{
1252 return QIconLoader::instance()->fallbackThemeName();
1253}
1254
1255/*!
1256 \since 5.12
1257
1258 Sets the fallback icon theme to \a name.
1259
1260 The fallback icon theme is consulted for icons not provided by
1261 the \l{themeName()}{current icon theme}, or if the \l{themeName()}
1262 {current icon theme} does not exist.
1263
1264 The \a name should correspond to theme in the same format
1265 as documented by setThemeName(), and will be looked up
1266 in themeSearchPaths().
1267
1268 \note Fallback icon themes should be set before creating
1269 QGuiApplication, to ensure correct initialization.
1270
1271 \sa fallbackThemeName(), themeSearchPaths(), themeName()
1272*/
1273void QIcon::setFallbackThemeName(const QString &name)
1274{
1275 QIconLoader::instance()->setFallbackThemeName(name);
1276}
1277
1278/*!
1279 \since 4.6
1280
1281 Returns the QIcon corresponding to \a name in the
1282 \l{themeName()}{current icon theme}.
1283
1284 If the current theme does not provide an icon for \a name,
1285 the \l{fallbackIconTheme()}{fallback icon theme} is consulted,
1286 before falling back to looking up standalone icon files in the
1287 \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
1288
1289 To fetch an icon from the current icon theme:
1290
1291 \snippet code/src_gui_image_qicon.cpp 3
1292
1293 If an \l{themeName()}{icon theme} has not been explicitly
1294 set via setThemeName() a platform defined icon theme will
1295 be used.
1296
1297 \note Platform icon themes is only implemented on
1298 \l{Freedesktop} based systems at the moment,
1299 following the \l{Freedesktop Icon Naming Specification}.
1300 In order to use themed icons on other platforms, you will have
1301 to bundle a \l{setThemeName()}{compliant theme} in one of your
1302 themeSearchPaths(), and set the appropriate themeName().
1303
1304 \externalpage https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
1305 \title Freedesktop Icon Naming Specification
1306
1307 \sa themeName(), fallbackIconTheme(), setThemeName(), themeSearchPaths(), fallbackSearchPaths()
1308*/
1309QIcon QIcon::fromTheme(const QString &name)
1310{
1311
1312 if (QIcon *cachedIcon = qtIconCache()->object(key: name))
1313 return *cachedIcon;
1314
1315 if (QDir::isAbsolutePath(path: name))
1316 return QIcon(name);
1317
1318 QIcon icon(new QThemeIconEngine(name));
1319 qtIconCache()->insert(key: name, object: new QIcon(icon));
1320 return icon;
1321}
1322
1323/*!
1324 \overload
1325
1326 Returns the QIcon corresponding to \a name in the
1327 \l{themeName()}{current icon theme}.
1328
1329 If the current theme does not provide an icon for \a name,
1330 the \l{fallbackIconTheme()}{fallback icon theme} is consulted,
1331 before falling back to looking up standalone icon files in the
1332 \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
1333
1334 If no icon is found \a fallback is returned.
1335
1336 This is useful to provide a guaranteed fallback, regardless of
1337 whether the current set of icon themes and fallbacks paths
1338 support the requested icon.
1339
1340 For example:
1341
1342 \snippet code/src_gui_image_qicon.cpp 4
1343
1344 \sa fallbackIconTheme(), fallbackSearchPaths()
1345*/
1346QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback)
1347{
1348 QIcon icon = fromTheme(name);
1349
1350 if (icon.isNull() || icon.availableSizes().isEmpty())
1351 return fallback;
1352
1353 return icon;
1354}
1355
1356/*!
1357 \since 4.6
1358
1359 Returns \c true if there is an icon available for \a name in the
1360 current icon theme or any of the fallbacks, as described by
1361 fromTheme(), otherwise returns \c false.
1362
1363 \sa themeSearchPaths(), fromTheme(), setThemeName()
1364*/
1365bool QIcon::hasThemeIcon(const QString &name)
1366{
1367 QIcon icon = fromTheme(name);
1368
1369 return icon.name() == name;
1370}
1371
1372/*!
1373 \since 5.6
1374
1375 Indicate that this icon is a mask image(boolean \a isMask), and hence can
1376 potentially be modified based on where it's displayed.
1377 \sa isMask()
1378*/
1379void QIcon::setIsMask(bool isMask)
1380{
1381 detach();
1382 if (!d)
1383 d = new QIconPrivate(new QPixmapIconEngine);
1384 d->is_mask = isMask;
1385}
1386
1387/*!
1388 \since 5.6
1389
1390 Returns \c true if this icon has been marked as a mask image.
1391 Certain platforms render mask icons differently (for example,
1392 menu icons on \macos).
1393
1394 \sa setIsMask()
1395*/
1396bool QIcon::isMask() const
1397{
1398 if (!d)
1399 return false;
1400 return d->is_mask;
1401}
1402
1403/*****************************************************************************
1404 QIcon stream functions
1405 *****************************************************************************/
1406#if !defined(QT_NO_DATASTREAM)
1407/*!
1408 \fn QDataStream &operator<<(QDataStream &stream, const QIcon &icon)
1409 \relates QIcon
1410 \since 4.2
1411
1412 Writes the given \a icon to the given \a stream as a PNG
1413 image. If the icon contains more than one image, all images will
1414 be written to the stream. Note that writing the stream to a file
1415 will not produce a valid image file.
1416*/
1417
1418QDataStream &operator<<(QDataStream &s, const QIcon &icon)
1419{
1420 if (s.version() >= QDataStream::Qt_4_3) {
1421 if (icon.isNull()) {
1422 s << QString();
1423 } else {
1424 s << icon.d->engine->key();
1425 icon.d->engine->write(out&: s);
1426 }
1427 } else if (s.version() == QDataStream::Qt_4_2) {
1428 if (icon.isNull()) {
1429 s << 0;
1430 } else {
1431 QPixmapIconEngine *engine = static_cast<QPixmapIconEngine *>(icon.d->engine);
1432 int num_entries = engine->pixmaps.size();
1433 s << num_entries;
1434 for (int i=0; i < num_entries; ++i) {
1435 s << engine->pixmaps.at(i).pixmap;
1436 s << engine->pixmaps.at(i).fileName;
1437 s << engine->pixmaps.at(i).size;
1438 s << (uint) engine->pixmaps.at(i).mode;
1439 s << (uint) engine->pixmaps.at(i).state;
1440 }
1441 }
1442 } else {
1443 s << QPixmap(icon.pixmap(w: 22,h: 22));
1444 }
1445 return s;
1446}
1447
1448/*!
1449 \fn QDataStream &operator>>(QDataStream &stream, QIcon &icon)
1450 \relates QIcon
1451 \since 4.2
1452
1453 Reads an image, or a set of images, from the given \a stream into
1454 the given \a icon.
1455*/
1456
1457QDataStream &operator>>(QDataStream &s, QIcon &icon)
1458{
1459 if (s.version() >= QDataStream::Qt_4_3) {
1460 icon = QIcon();
1461 QString key;
1462 s >> key;
1463 if (key == "QPixmapIconEngine"_L1) {
1464 icon.d = new QIconPrivate(new QPixmapIconEngine);
1465 icon.d->engine->read(in&: s);
1466 } else if (key == "QIconLoaderEngine"_L1 || key == "QThemeIconEngine"_L1) {
1467 icon.d = new QIconPrivate(new QThemeIconEngine);
1468 icon.d->engine->read(in&: s);
1469 } else {
1470 const int index = iceLoader()->indexOf(needle: key);
1471 if (index != -1) {
1472 if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(object: iceLoader()->instance(index))) {
1473 if (QIconEngine *engine= factory->create()) {
1474 icon.d = new QIconPrivate(engine);
1475 engine->read(in&: s);
1476 } // factory
1477 } // instance
1478 } // index
1479 }
1480 } else if (s.version() == QDataStream::Qt_4_2) {
1481 icon = QIcon();
1482 int num_entries;
1483 QPixmap pm;
1484 QString fileName;
1485 QSize sz;
1486 uint mode;
1487 uint state;
1488
1489 s >> num_entries;
1490 for (int i=0; i < num_entries; ++i) {
1491 s >> pm;
1492 s >> fileName;
1493 s >> sz;
1494 s >> mode;
1495 s >> state;
1496 if (pm.isNull())
1497 icon.addFile(fileName, size: sz, mode: QIcon::Mode(mode), state: QIcon::State(state));
1498 else
1499 icon.addPixmap(pixmap: pm, mode: QIcon::Mode(mode), state: QIcon::State(state));
1500 }
1501 } else {
1502 QPixmap pm;
1503 s >> pm;
1504 icon.addPixmap(pixmap: pm);
1505 }
1506 return s;
1507}
1508
1509#endif //QT_NO_DATASTREAM
1510
1511#ifndef QT_NO_DEBUG_STREAM
1512QDebug operator<<(QDebug dbg, const QIcon &i)
1513{
1514 QDebugStateSaver saver(dbg);
1515 dbg.resetFormat();
1516 dbg.nospace();
1517 dbg << "QIcon(";
1518 if (i.isNull()) {
1519 dbg << "null";
1520 } else {
1521 if (!i.name().isEmpty())
1522 dbg << i.name() << ',';
1523 dbg << "availableSizes[normal,Off]=" << i.availableSizes()
1524 << ",cacheKey=" << Qt::showbase << Qt::hex << i.cacheKey() << Qt::dec << Qt::noshowbase;
1525 }
1526 dbg << ')';
1527 return dbg;
1528}
1529#endif
1530
1531/*!
1532 \fn DataPtr &QIcon::data_ptr()
1533 \internal
1534*/
1535
1536/*!
1537 \typedef QIcon::DataPtr
1538 \internal
1539*/
1540
1541/*!
1542 \internal
1543 \since 5.6
1544 Attempts to find a suitable @Nx file for the given \a targetDevicePixelRatio
1545 Returns the \a baseFileName if no such file was found.
1546
1547 Given base foo.png and a target dpr of 2.5, this function will look for
1548 foo@3x.png, then foo@2x, then fall back to foo.png if not found.
1549
1550 \a sourceDevicePixelRatio will be set to the value of N if the argument is
1551 not \nullptr
1552*/
1553QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
1554 qreal *sourceDevicePixelRatio)
1555{
1556 if (targetDevicePixelRatio <= 1.0)
1557 return baseFileName;
1558
1559 static bool disableNxImageLoading = !qEnvironmentVariableIsEmpty(varName: "QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
1560 if (disableNxImageLoading)
1561 return baseFileName;
1562
1563 int dotIndex = baseFileName.lastIndexOf(c: u'.');
1564 if (dotIndex == -1) { /* no dot */
1565 dotIndex = baseFileName.size(); /* append */
1566 } else if (dotIndex >= 2 && baseFileName[dotIndex - 1] == u'9'
1567 && baseFileName[dotIndex - 2] == u'.') {
1568 // If the file has a .9.* (9-patch image) extension, we must ensure that the @nx goes before it.
1569 dotIndex -= 2;
1570 }
1571
1572 QString atNxfileName = baseFileName;
1573 atNxfileName.insert(i: dotIndex, s: "@2x"_L1);
1574 // Check for @Nx, ..., @3x, @2x file versions,
1575 for (int n = qMin(a: qCeil(v: targetDevicePixelRatio), b: 9); n > 1; --n) {
1576 atNxfileName[dotIndex + 1] = QLatin1Char('0' + n);
1577 if (QFile::exists(fileName: atNxfileName)) {
1578 if (sourceDevicePixelRatio)
1579 *sourceDevicePixelRatio = n;
1580 return atNxfileName;
1581 }
1582 }
1583
1584 return baseFileName;
1585}
1586
1587QT_END_NAMESPACE
1588#endif //QT_NO_ICON
1589

source code of qtbase/src/gui/image/qicon.cpp