Merge lp:~lukas-kde/ubuntu-ui-toolkit/staging-fix-icon-svg-image-loading into lp:ubuntu-ui-toolkit/staging
- staging-fix-icon-svg-image-loading
- Merge into staging
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Tim Peeters | ||||||||
Approved revision: | 1966 | ||||||||
Merged at revision: | 1968 | ||||||||
Proposed branch: | lp:~lukas-kde/ubuntu-ui-toolkit/staging-fix-icon-svg-image-loading | ||||||||
Merge into: | lp:ubuntu-ui-toolkit/staging | ||||||||
Diff against target: |
318 lines (+96/-66) 4 files modified
src/Ubuntu/Components/plugin/unitythemeiconprovider.cpp (+61/-51) src/Ubuntu/Components/plugin/unitythemeiconprovider.h (+1/-1) tests/unit_x11/tst_components/tst_icon.qml (+19/-0) tests/unit_x11/tst_iconprovider/tst_iconprovider.cpp (+15/-14) |
||||||||
To merge this branch: | bzr merge lp:~lukas-kde/ubuntu-ui-toolkit/staging-fix-icon-svg-image-loading | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
ubuntu-sdk-build-bot | continuous-integration | Approve | |
Tim Peeters | Approve | ||
Review via email: mp+293281@code.launchpad.net |
Commit message
Fix some image scaling issues by reusing Qt code (QImage instead of QPixmap)
Description of the change
Adjust the UnityThemeIconP
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1965
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1965
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1965
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1965
https:/
Executed test runs:
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Tim Peeters (tpeeters) wrote : | # |
I tried the test code from https:/
tim@tim-
file://
Tim Peeters (tpeeters) wrote : | # |
Regression tests for the bugs are missing.
Lukáš Tinkl (lukas-kde) wrote : | # |
I unlinked the bug as we're not changing the DPR currently anywhere.
- 1966. By Lukáš Tinkl
-
add a regression test for #1421293
Lukáš Tinkl (lukas-kde) wrote : | # |
Added a regression test in tst_icon.qml, based on original Albert's testcase attached to lpbug:1421293. With this branch, icon loading no longer exhibits the faulty behavior that could be originally seen in https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Tim Peeters (tpeeters) wrote : | # |
Code looks good. We still need to do manual testing of apps on devices to make sure it doesn't break anything.
Tim Peeters (tpeeters) wrote : | # |
Tested by unity8 team during sprint last week. Happroving.
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1966
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'src/Ubuntu/Components/plugin/unitythemeiconprovider.cpp' |
2 | --- src/Ubuntu/Components/plugin/unitythemeiconprovider.cpp 2016-03-16 14:24:15 +0000 |
3 | +++ src/Ubuntu/Components/plugin/unitythemeiconprovider.cpp 2016-04-29 15:29:47 +0000 |
4 | @@ -21,8 +21,7 @@ |
5 | #include <QDir> |
6 | #include <QFileInfo> |
7 | #include <QSettings> |
8 | -#include <QSvgRenderer> |
9 | -#include <QPainter> |
10 | +#include <QImageReader> |
11 | #include <QStandardPaths> |
12 | #include <QtDebug> |
13 | |
14 | @@ -48,27 +47,27 @@ |
15 | // Does a breadth-first search for an icon with any name in @names. Parent |
16 | // themes are only looked at if the current theme doesn't contain any icon |
17 | // in @names. |
18 | - QPixmap findBestIcon(const QStringList &names, const QSize &size, QSet<QString> *alreadySearchedThemes) |
19 | + QImage findBestIcon(const QStringList &names, QSize *impsize, const QSize &size, QSet<QString> *alreadySearchedThemes) |
20 | { |
21 | if (alreadySearchedThemes) { |
22 | if (alreadySearchedThemes->contains(name)) |
23 | - return QPixmap(); |
24 | + return QImage(); |
25 | alreadySearchedThemes->insert(name); |
26 | } |
27 | |
28 | Q_FOREACH(const QString &name, names) { |
29 | - QPixmap pixmap = lookupIcon(name, size); |
30 | - if (!pixmap.isNull()) |
31 | - return pixmap; |
32 | + QImage image = lookupIcon(name, impsize, size); |
33 | + if (!image.isNull()) |
34 | + return image; |
35 | } |
36 | |
37 | Q_FOREACH(IconThemePointer theme, parents) { |
38 | - QPixmap pixmap = theme->findBestIcon(names, size, alreadySearchedThemes); |
39 | - if (!pixmap.isNull()) |
40 | - return pixmap; |
41 | + QImage image = theme->findBestIcon(names, impsize, size, alreadySearchedThemes); |
42 | + if (!image.isNull()) |
43 | + return image; |
44 | } |
45 | |
46 | - return QPixmap(); |
47 | + return QImage(); |
48 | } |
49 | |
50 | private: |
51 | @@ -82,7 +81,7 @@ |
52 | |
53 | IconTheme(const QString &name): name(name) |
54 | { |
55 | - QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); |
56 | + const QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); |
57 | |
58 | Q_FOREACH(const QString &path, paths) { |
59 | QDir dir(path + "/icons/" + name); |
60 | @@ -130,30 +129,42 @@ |
61 | return Fixed; |
62 | } |
63 | |
64 | - static QPixmap loadIcon(const QString &filename, const QSize &size) |
65 | + static QImage loadIcon(const QString &filename, QSize *impsize, const QSize &requestSize) |
66 | { |
67 | - QPixmap pixmap; |
68 | - |
69 | - const bool anyZero = size.width() <= 0 || size.height() <= 0; |
70 | - const Qt::AspectRatioMode scaleMode = anyZero ? Qt::KeepAspectRatioByExpanding : Qt::KeepAspectRatio; |
71 | - |
72 | - if (filename.endsWith(QLatin1String(".png"))) { |
73 | - pixmap = QPixmap(filename); |
74 | - if (!pixmap.isNull() && !size.isNull() && (pixmap.width() != size.width() || pixmap.height() != size.height())) { |
75 | - const QSize newSize = pixmap.size().scaled(size.width(), size.height(), scaleMode); |
76 | - pixmap = pixmap.scaled(newSize); |
77 | - } |
78 | - } |
79 | - else if (filename.endsWith(QLatin1String(".svg"))) { |
80 | - QSvgRenderer renderer(filename); |
81 | - pixmap = QPixmap(renderer.defaultSize().scaled(size.width(), size.height(), scaleMode)); |
82 | - pixmap.fill(Qt::transparent); |
83 | - QPainter painter(&pixmap); |
84 | - renderer.render(&painter); |
85 | - painter.end(); |
86 | - } |
87 | - |
88 | - return pixmap; |
89 | + QImage image; |
90 | + QImageReader imgio(filename); |
91 | + |
92 | + const bool force_scale = imgio.format() == QStringLiteral("svg") |
93 | + || imgio.format() == QStringLiteral("svgz"); |
94 | + |
95 | + if (requestSize.width() > 0 || requestSize.height() > 0) { |
96 | + QSize s = imgio.size(); |
97 | + qreal ratio = 0.0; |
98 | + if (requestSize.width() > 0 && (force_scale || requestSize.width() < s.width())) { |
99 | + ratio = qreal(requestSize.width())/s.width(); |
100 | + } |
101 | + if (requestSize.height() > 0 && (force_scale || requestSize.height() < s.height())) { |
102 | + qreal hr = qreal(requestSize.height())/s.height(); |
103 | + if (ratio == 0.0 || hr < ratio) |
104 | + ratio = hr; |
105 | + } |
106 | + if (ratio > 0.0) { |
107 | + s.setHeight(qRound(s.height() * ratio)); |
108 | + s.setWidth(qRound(s.width() * ratio)); |
109 | + imgio.setScaledSize(s); |
110 | + } |
111 | + } |
112 | + |
113 | + if (impsize) |
114 | + *impsize = imgio.scaledSize(); |
115 | + |
116 | + if (imgio.read(&image)) { |
117 | + if (impsize) |
118 | + *impsize = image.size(); |
119 | + return image; |
120 | + } else { |
121 | + return QImage(); |
122 | + } |
123 | } |
124 | |
125 | QString lookupIconFile(const QString &dir, const QString &name) |
126 | @@ -174,16 +185,16 @@ |
127 | return QString(); |
128 | } |
129 | |
130 | - QPixmap lookupIcon(const QString &iconName, const QSize &size) |
131 | + QImage lookupIcon(const QString &iconName, QSize *impsize, const QSize &size) |
132 | { |
133 | const int iconSize = qMax(size.width(), size.height()); |
134 | if (iconSize > 0) |
135 | - return lookupBestMatchingIcon(iconName, size); |
136 | + return lookupBestMatchingIcon(iconName, impsize, size); |
137 | else |
138 | - return lookupLargestIcon(iconName); |
139 | + return lookupLargestIcon(iconName, impsize); |
140 | } |
141 | |
142 | - QPixmap lookupBestMatchingIcon(const QString &iconName, const QSize &size) |
143 | + QImage lookupBestMatchingIcon(const QString &iconName, QSize *impsize, const QSize &size) |
144 | { |
145 | int minDistance = 10000; |
146 | QString bestFilename; |
147 | @@ -205,12 +216,12 @@ |
148 | } |
149 | |
150 | if (!bestFilename.isNull()) |
151 | - return loadIcon(bestFilename, size); |
152 | + return loadIcon(bestFilename, impsize, size); |
153 | |
154 | - return QPixmap(); |
155 | + return QImage(); |
156 | } |
157 | |
158 | - QPixmap lookupLargestIcon(const QString &iconName) |
159 | + QImage lookupLargestIcon(const QString &iconName, QSize *impsize) |
160 | { |
161 | int maxSize = 0; |
162 | QString bestFilename; |
163 | @@ -228,9 +239,9 @@ |
164 | } |
165 | |
166 | if (!bestFilename.isNull()) |
167 | - return loadIcon(bestFilename, QSize(maxSize, maxSize)); |
168 | + return loadIcon(bestFilename, impsize, QSize(maxSize, maxSize)); |
169 | |
170 | - return QPixmap(); |
171 | + return QImage(); |
172 | } |
173 | |
174 | int directorySizeDistance(const Directory &dir, const QSize &iconSize) |
175 | @@ -258,24 +269,23 @@ |
176 | }; |
177 | |
178 | UnityThemeIconProvider::UnityThemeIconProvider(const QString &themeName): |
179 | - QQuickImageProvider(QQuickImageProvider::Pixmap) |
180 | + QQuickImageProvider(QQuickImageProvider::Image) |
181 | { |
182 | theme = IconTheme::get(themeName); |
183 | } |
184 | |
185 | -QPixmap UnityThemeIconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) |
186 | +QImage UnityThemeIconProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) |
187 | { |
188 | // The hicolor theme will be searched last as per |
189 | // https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html |
190 | QSet<QString> alreadySearchedThemes; |
191 | const QStringList names = id.split(QLatin1Char(','), QString::SkipEmptyParts); |
192 | - QPixmap pixmap = theme->findBestIcon(names, requestedSize, &alreadySearchedThemes); |
193 | + QImage image = theme->findBestIcon(names, size, requestedSize, &alreadySearchedThemes); |
194 | |
195 | - if (pixmap.isNull()) { |
196 | + if (image.isNull()) { |
197 | IconTheme::IconThemePointer theme = IconTheme::get(QStringLiteral("hicolor")); |
198 | - return theme->findBestIcon(names, requestedSize, nullptr); |
199 | + return theme->findBestIcon(names, size, requestedSize, nullptr); |
200 | } |
201 | |
202 | - *size = pixmap.size(); |
203 | - return pixmap; |
204 | + return image; |
205 | } |
206 | |
207 | === modified file 'src/Ubuntu/Components/plugin/unitythemeiconprovider.h' |
208 | --- src/Ubuntu/Components/plugin/unitythemeiconprovider.h 2016-03-16 14:24:15 +0000 |
209 | +++ src/Ubuntu/Components/plugin/unitythemeiconprovider.h 2016-04-29 15:29:47 +0000 |
210 | @@ -25,7 +25,7 @@ |
211 | { |
212 | public: |
213 | UnityThemeIconProvider(const QString &themeName = QStringLiteral("suru")); |
214 | - QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); |
215 | + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); |
216 | |
217 | private: |
218 | QSharedPointer<class IconTheme> theme; |
219 | |
220 | === added file 'tests/unit_x11/tst_components/logo.png' |
221 | Binary files tests/unit_x11/tst_components/logo.png 1970-01-01 00:00:00 +0000 and tests/unit_x11/tst_components/logo.png 2016-04-29 15:29:47 +0000 differ |
222 | === modified file 'tests/unit_x11/tst_components/tst_icon.qml' |
223 | --- tests/unit_x11/tst_components/tst_icon.qml 2016-02-02 10:19:17 +0000 |
224 | +++ tests/unit_x11/tst_components/tst_icon.qml 2016-04-29 15:29:47 +0000 |
225 | @@ -58,6 +58,19 @@ |
226 | height: width |
227 | source: Qt.resolvedUrl("tst_icon-select.png") |
228 | } |
229 | + |
230 | + // Regression test for bug #1421293 |
231 | + Icon { |
232 | + id: icon4 |
233 | + height: units.gu(2) |
234 | + source: Qt.resolvedUrl("logo.png") |
235 | + } |
236 | + Icon { |
237 | + id: icon5 |
238 | + height: units.gu(2) |
239 | + width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth |
240 | + source: Qt.resolvedUrl("logo.png") |
241 | + } |
242 | } |
243 | |
244 | UbuntuTestCase { |
245 | @@ -131,5 +144,11 @@ |
246 | icon.color = Qt.rgba(0.0, 0.0, 0.0, 0.0); |
247 | compare(shader.visible, false); |
248 | } |
249 | + |
250 | + function test_iconScaling_bug1421293() { |
251 | + // make sure both icons end up the same size |
252 | + compare(icon4.width, icon5.width); |
253 | + compare(icon4.height, icon5.height); |
254 | + } |
255 | } |
256 | } |
257 | |
258 | === modified file 'tests/unit_x11/tst_iconprovider/tst_iconprovider.cpp' |
259 | --- tests/unit_x11/tst_iconprovider/tst_iconprovider.cpp 2016-03-16 15:02:35 +0000 |
260 | +++ tests/unit_x11/tst_iconprovider/tst_iconprovider.cpp 2016-04-29 15:29:47 +0000 |
261 | @@ -39,14 +39,15 @@ |
262 | QTest::addColumn<QSize>("requestSize"); |
263 | QTest::addColumn<QSize>("resultSize"); |
264 | |
265 | - QTest::newRow("battery0") << "battery-100-charging" << QSize(-1, -1) << QSize(256, 165); |
266 | - QTest::newRow("battery1") << "battery-100-charging" << QSize(-1, 16) << QSize(24, 16); |
267 | + QTest::newRow("battery0") << "battery-100-charging" << QSize(-1, -1) << QSize(256, 166); |
268 | + QTest::newRow("battery1") << "battery-100-charging" << QSize(-1, 16) << QSize(25, 16); |
269 | QTest::newRow("battery2") << "battery-100-charging" << QSize(16, -1) << QSize(16, 10); |
270 | - QTest::newRow("battery3") << "battery-100-charging" << QSize(0, 16) << QSize(24, 16); |
271 | + QTest::newRow("battery3") << "battery-100-charging" << QSize(0, 16) << QSize(25, 16); |
272 | QTest::newRow("battery4") << "battery-100-charging" << QSize(16, 0) << QSize(16, 10); |
273 | QTest::newRow("battery5") << "battery-100-charging" << QSize(24, 16) << QSize(24, 16); |
274 | - QTest::newRow("battery6") << "battery-100-charging" << QSize(24, 24) << QSize(24, 15); |
275 | + QTest::newRow("battery6") << "battery-100-charging" << QSize(24, 24) << QSize(24, 16); |
276 | QTest::newRow("battery7") << "battery-100-charging" << QSize(37, 24) << QSize(37, 24); |
277 | + QTest::newRow("battery8") << "battery-100-charging" << QSize(15, 512) << QSize(15, 10); |
278 | |
279 | QTest::newRow("gallery0") << "gallery-app" << QSize(-1, -1) << QSize(512, 512); |
280 | QTest::newRow("gallery1") << "gallery-app" << QSize(-1, 16) << QSize(16, 16); |
281 | @@ -65,13 +66,13 @@ |
282 | |
283 | UnityThemeIconProvider provider("mockTheme"); |
284 | QSize returnedSize; |
285 | - QPixmap p = provider.requestPixmap(icon, &returnedSize, requestSize); |
286 | - QCOMPARE(p.size(), resultSize); |
287 | + QImage i = provider.requestImage(icon, &returnedSize, requestSize); |
288 | + QCOMPARE(i.size(), resultSize); |
289 | QCOMPARE(returnedSize, resultSize); |
290 | |
291 | // Search again to make sure subsequent searches still work |
292 | - p = provider.requestPixmap(icon, &returnedSize, requestSize); |
293 | - QCOMPARE(p.size(), resultSize); |
294 | + i = provider.requestImage(icon, &returnedSize, requestSize); |
295 | + QCOMPARE(i.size(), resultSize); |
296 | QCOMPARE(returnedSize, resultSize); |
297 | } |
298 | |
299 | @@ -82,14 +83,14 @@ |
300 | |
301 | // myapp is in MockTheme3 (white) and hicolor (black) |
302 | // MockTheme3 one is returned since hicolor is last per spec |
303 | - QPixmap p = provider.requestPixmap("myapp", &returnedSize, QSize(-1, -1)); |
304 | - QVERIFY(!p.isNull()); |
305 | - QCOMPARE(QColor(p.toImage().pixel(0,0)), QColor(Qt::white)); |
306 | + QImage i = provider.requestImage("myapp", &returnedSize, QSize(-1, -1)); |
307 | + QVERIFY(!i.isNull()); |
308 | + QCOMPARE(QColor(i.pixel(0,0)), QColor(Qt::white)); |
309 | |
310 | // myapp2 is only in hicolor (black) so that's the one returned |
311 | - p = provider.requestPixmap("myapp2", &returnedSize, QSize(-1, -1)); |
312 | - QVERIFY(!p.isNull()); |
313 | - QCOMPARE(QColor(p.toImage().pixel(0,0)), QColor(Qt::black)); |
314 | + i = provider.requestImage("myapp2", &returnedSize, QSize(-1, -1)); |
315 | + QVERIFY(!i.isNull()); |
316 | + QCOMPARE(QColor(i.pixel(0,0)), QColor(Qt::black)); |
317 | } |
318 | }; |
319 |
PASSED: Continuous integration, rev:1965 /jenkins. ubuntu. com/ubuntu- sdk/job/ ubuntu- ui-toolkit- ci-amd64- stable/ 779/ /jenkins. ubuntu. com/ubuntu- sdk/job/ generic- update- mp/3076/ console
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/ubuntu- sdk/job/ ubuntu- ui-toolkit- ci-amd64- stable/ 779/rebuild
https:/