1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <qicon.h>
31
32class tst_QIconHighDpi : public QObject
33{
34 Q_OBJECT
35public:
36 tst_QIconHighDpi();
37
38private slots:
39 void initTestCase();
40 void fromTheme_data();
41 void fromTheme();
42 void ninePatch();
43};
44
45tst_QIconHighDpi::tst_QIconHighDpi()
46{
47}
48
49void tst_QIconHighDpi::initTestCase()
50{
51}
52
53void tst_QIconHighDpi::fromTheme_data()
54{
55 QTest::addColumn<int>(name: "requestedSize");
56 QTest::addColumn<int>(name: "expectedSize");
57 QTest::addColumn<qreal>(name: "expectedDpr");
58
59 // The pixmaps that we have available can be found in tst_qiconhighdpi.qrc.
60 // Currently we only have @1 and @2 icons available.
61 const int dpr = qCeil(qApp->devicePixelRatio());
62
63 // We have an @2 high DPI version of the 22x22 size of this icon.
64 switch (dpr) {
65 case 1: QTest::newRow(dataTag: "22x22,dpr=1") << 22 << 22 << 1.0; break;
66 case 2: QTest::newRow(dataTag: "22x22,dpr=2") << 22 << 44 << 2.0; break;
67 case 3: QTest::newRow(dataTag: "22x22,dpr=3") << 22 << 44 << 2.0; break;
68 }
69
70 // We don't have a high DPI version of the 16x16 size of this icon,
71 // so directoryMatchesSize() will return false for all directories.
72 // directorySizeDistance() is then called to find the best match.
73 // The table below illustrates the results for our available images at various DPRs:
74 // Available size | Available scale | Requested size | Requested scale | Distance
75 // 22 * 2 - 16 * 1 = 28
76 // 22 * 1 - 16 * 1 = 6
77 // 16 * 1 - 16 * 1 = 0 < (16x16)
78 // Available size | Available scale | Requested size | Requested scale | Distance
79 // 22 * 2 - 16 * 2 = 12
80 // 22 * 1 - 16 * 2 = 10 < (22x22)
81 // 16 * 1 - 16 * 2 = 16
82 // Available size | Available scale | Requested size | Requested scale | Distance
83 // 22 * 2 - 16 * 3 = 4 < (22x22@2)
84 // 22 * 1 - 16 * 3 = 26
85 // 16 * 1 - 16 * 3 = 32
86 // Both of these functions are implementations of the freedesktop icon theme spec,
87 // which dictates that if there is no matching scale, directorySizeDistance() determines
88 // the winner, regardless of whether or not the scale is too low for the requested scale.
89 switch (dpr) {
90 case 1: QTest::newRow(dataTag: "16x16,dpr=1") << 16 << 16 << 1.0; break;
91 // PixmapEntry::pixmap() will only downscale the pixmap if actualSize.width() > size.width().
92 // In this case, 22 > 32 is false, so a 22x22 pixmap is returned.
93 case 2: QTest::newRow(dataTag: "16x16,dpr=2") << 16 << 22 << 1.375; break;
94 case 3: QTest::newRow(dataTag: "16x16,dpr=3") << 16 << 44 << 2.75; break;
95 }
96
97 // We don't have an 8x8 size of this icon, so:
98 // Available size | Available scale | Requested size | Requested scale | Distance
99 // 22 * 2 - 8 * 1 = 36
100 // 22 * 1 - 8 * 1 = 14
101 // 16 * 1 - 8 * 1 = 8 < (16x16)
102 // Available size | Available scale | Requested size | Requested scale | Distance
103 // 22 * 2 - 8 * 2 = 28
104 // 22 * 1 - 8 * 2 = 6
105 // 16 * 1 - 8 * 2 = 0 < (16x16)
106 // Available size | Available scale | Requested size | Requested scale | Distance
107 // 22 * 2 - 8 * 3 = 20
108 // 22 * 1 - 8 * 3 = 2 < (22x22)
109 // 16 * 1 - 8 * 3 = 8
110 switch (dpr) {
111 case 1: QTest::newRow(dataTag: "8x8,dpr=1") << 8 << 8 << 1.0; break;
112 case 2: QTest::newRow(dataTag: "8x8,dpr=2") << 8 << 16 << 2.0; break;
113 case 3: QTest::newRow(dataTag: "8x8,dpr=3") << 8 << 22 << 2.75; break;
114 }
115
116 // We don't have a 44x44 size of this icon, so:
117 // Available size | Available scale | Requested size | Requested scale | Distance
118 // 22 * 2 - 44 * 1 = 0 < (22x22@2)
119 // 22 * 1 - 44 * 1 = 22
120 // 16 * 1 - 44 * 1 = 28
121 // Available size | Available scale | Requested size | Requested scale | Distance
122 // 22 * 2 - 44 * 2 = 44 < (22x22@2)
123 // 22 * 1 - 44 * 2 = 66
124 // 16 * 1 - 44 * 2 = 72
125 // Available size | Available scale | Requested size | Requested scale | Distance
126 // 22 * 2 - 44 * 3 = 88 < (22x22@2)
127 // 22 * 1 - 44 * 3 = 110
128 // 16 * 1 - 44 * 3 = 116
129 switch (dpr) {
130 case 1: QTest::newRow(dataTag: "44x44,dpr=1") << 44 << 44 << 1.0; break;
131 case 2: QTest::newRow(dataTag: "44x44,dpr=2") << 44 << 44 << 1.0; break;
132 case 3: QTest::newRow(dataTag: "44x44,dpr=3") << 44 << 44 << 1.0; break;
133 }
134
135 // We don't have a 20x20 size of this icon, so:
136 // Available size | Available scale | Requested size | Requested scale | Distance
137 // 22 * 2 - 20 * 1 = 24
138 // 22 * 1 - 20 * 1 = 2 < (22x22)
139 // 16 * 1 - 20 * 1 = 4
140 // Available size | Available scale | Requested size | Requested scale | Distance
141 // 22 * 2 - 20 * 2 = 4 < (22x22@2)
142 // 22 * 1 - 20 * 2 = 18
143 // 16 * 1 - 20 * 2 = 24
144 // Available size | Available scale | Requested size | Requested scale | Distance
145 // 22 * 2 - 20 * 3 = 16 < (22x22@2)
146 // 22 * 1 - 20 * 3 = 38
147 // 16 * 1 - 20 * 3 = 44
148 switch (dpr) {
149 case 1: QTest::newRow(dataTag: "20x20,dpr=1") << 20 << 20 << 1.0; break;
150 // PixmapEntry::pixmap() will only downscale the pixmap if actualSize.width() > size.width().
151 // In this case, 44 > 40 is true, so the 44x44 pixmap is downscaled to 40x40.
152 case 2: QTest::newRow(dataTag: "20x20,dpr=2") << 20 << 40 << 2.0; break;
153 case 3: QTest::newRow(dataTag: "20x20,dpr=3") << 20 << 44 << 2.2; break;
154 }
155}
156
157void tst_QIconHighDpi::fromTheme()
158{
159 QFETCH(int, requestedSize);
160 QFETCH(int, expectedSize);
161 QFETCH(qreal, expectedDpr);
162
163 QString searchPath = QLatin1String(":/icons");
164 QIcon::setThemeSearchPaths(QStringList() << searchPath);
165 QCOMPARE(QIcon::themeSearchPaths().size(), 1);
166 QCOMPARE(searchPath, QIcon::themeSearchPaths()[0]);
167
168 QString themeName("testtheme");
169 QIcon::setThemeName(themeName);
170 QCOMPARE(QIcon::themeName(), themeName);
171
172 QIcon appointmentIcon = QIcon::fromTheme(name: "appointment-new");
173 QVERIFY(!appointmentIcon.isNull());
174 QVERIFY(!appointmentIcon.availableSizes(QIcon::Normal, QIcon::Off).isEmpty());
175 QVERIFY(appointmentIcon.availableSizes().contains(QSize(16, 16)));
176 QVERIFY(appointmentIcon.availableSizes().contains(QSize(22, 22)));
177
178 const QPixmap pixmap = appointmentIcon.pixmap(extent: requestedSize);
179 QCOMPARE(pixmap.size(), QSize(expectedSize, expectedSize));
180 // We should get the high DPI version of an image if it exists in the correct directory.
181 // Note that we didn't pass the QWindow to QIcon::pixmap(),
182 // because QGuiApplication::devicePixelRatio() will be used if no window was given.
183 QCOMPARE(pixmap.devicePixelRatio(), expectedDpr);
184}
185
186void tst_QIconHighDpi::ninePatch()
187{
188 const QIcon icon(":/icons/misc/button.9.png");
189 const int dpr = qCeil(qApp->devicePixelRatio());
190
191 switch (dpr) {
192 case 1:
193 QCOMPARE(icon.availableSizes().size(), 1);
194 QCOMPARE(icon.availableSizes().at(0), QSize(42, 42));
195 break;
196 case 2:
197 QCOMPARE(icon.availableSizes().size(), 2);
198 QCOMPARE(icon.availableSizes().at(0), QSize(42, 42));
199 QCOMPARE(icon.availableSizes().at(1), QSize(82, 82));
200 break;
201 }
202}
203
204int main(int argc, char *argv[])
205{
206 QGuiApplication::setAttribute(attribute: Qt::AA_UseHighDpiPixmaps);
207 QGuiApplication app(argc, argv);
208 Q_UNUSED(app);
209 tst_QIconHighDpi test;
210 QTEST_SET_MAIN_SOURCE_PATH
211 return QTest::qExec(testObject: &test, argc, argv);
212}
213
214#include "tst_qiconhighdpi.moc"
215

source code of qtbase/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp