Merge lp:~lukas-kde/ubuntu-ui-toolkit/staging-fix-icon-svg-image-loading into lp:ubuntu-ui-toolkit/staging

Proposed by Lukáš Tinkl
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
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 UnityThemeIconProvider to duplicate Qt's own image loading behavior, fixes some image scaling issues

To post a comment you must log in.
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

I tried the test code from https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1561654 and the bug is still present:

tim@tim-mbp:~/dev/ubuntu-ui-toolkit/r/staging-fix-icon-svg-image-loading$ QT_DEVICE_PIXEL_RATIO=2 qmlscene main.qml
file:///home/tim/dev/ubuntu-ui-toolkit/r/staging-fix-icon-svg-image-loading/main.qml:6:5: QML Icon: Binding loop detected for property "implicitHeight"

review: Needs Fixing
Revision history for this message
Tim Peeters (tpeeters) wrote :

Regression tests for the bugs are missing.

Revision history for this message
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

Revision history for this message
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://launchpadlibrarian.net/197431959/icon.png

Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

review: Approve
Revision history for this message
Tim Peeters (tpeeters) wrote :

Tested by unity8 team during sprint last week. Happroving.

Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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'
221Binary 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

Subscribers

People subscribed via source and target branches