Merge lp:~aacid/unity8/croppedImageMinimumSourceSizeProvider into lp:unity8
- croppedImageMinimumSourceSizeProvider
- Merge into trunk
Proposed by
Albert Astals Cid
Status: | Work in progress |
---|---|
Proposed branch: | lp:~aacid/unity8/croppedImageMinimumSourceSizeProvider |
Merge into: | lp:unity8 |
Diff against target: |
431 lines (+346/-6) 6 files modified
plugins/Dash/CMakeLists.txt (+1/-0) plugins/Dash/CardCreator.js (+6/-6) plugins/Dash/croppedimageminimumsizeimageprovider.cpp (+294/-0) plugins/Dash/croppedimageminimumsizeimageprovider.h (+36/-0) plugins/Dash/plugin.cpp (+8/-0) plugins/Dash/plugin.h (+1/-0) |
To merge this branch: | bzr merge lp:~aacid/unity8/croppedImageMinimumSourceSizeProvider |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity Team | Pending | ||
Review via email: mp+300176@code.launchpad.net |
Commit message
Description of the change
Image Provider for PreserveAspectCrop fillmode
To post a comment you must log in.
Unmerged revisions
- 2537. By Albert Astals Cid
-
First attempt at an image provider that does what CroppedImageMin
imumSourceSize does Saves double loading of files in the "bad" case
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'plugins/Dash/CMakeLists.txt' |
2 | --- plugins/Dash/CMakeLists.txt 2016-06-02 09:32:33 +0000 |
3 | +++ plugins/Dash/CMakeLists.txt 2016-07-15 11:14:48 +0000 |
4 | @@ -28,6 +28,7 @@ |
5 | verticaljournal.cpp |
6 | horizontaljournal.cpp |
7 | organicgrid.cpp |
8 | + croppedimageminimumsizeimageprovider.cpp |
9 | ) |
10 | |
11 | add_library(Dash-qml MODULE |
12 | |
13 | === modified file 'plugins/Dash/CardCreator.js' |
14 | --- plugins/Dash/CardCreator.js 2016-06-17 01:18:49 +0000 |
15 | +++ plugins/Dash/CardCreator.js 2016-07-15 11:14:48 +0000 |
16 | @@ -104,10 +104,10 @@ |
17 | %6 \n\ |
18 | width: root.fixedArtShapeSize.width; \n\ |
19 | height: root.fixedArtShapeSize.height; \n\ |
20 | - CroppedImageMinimumSourceSize { \n\ |
21 | + Image { \n\ |
22 | id: artImage; \n\ |
23 | objectName: "artImage"; \n\ |
24 | - source: artShapeLoader.cardArt; \n\ |
25 | + source: "image://croppedImageMinimumSize/" + artShapeLoader.cardArt; \n\ |
26 | asynchronous: %5; \n\ |
27 | visible: %4; \n\ |
28 | width: %2; \n\ |
29 | @@ -147,10 +147,10 @@ |
30 | %6 \n\ |
31 | width: image.status !== Image.Ready ? 0 : image.width; \n\ |
32 | height: image.status !== Image.Ready ? 0 : image.height; \n\ |
33 | - CroppedImageMinimumSourceSize { \n\ |
34 | + Image { \n\ |
35 | id: artImage; \n\ |
36 | objectName: "artImage"; \n\ |
37 | - source: artShapeLoader.cardArt; \n\ |
38 | + source: "image://croppedImageMinimumSize/" + artShapeLoader.cardArt; \n\ |
39 | asynchronous: %5; \n\ |
40 | visible: %4; \n\ |
41 | width: %2; \n\ |
42 | @@ -285,11 +285,11 @@ |
43 | // %2 is used as visible of mascotImage |
44 | // %3 is injected as code to mascotImage |
45 | // %4 is used as fallback image |
46 | -var kMascotImageCode = 'CroppedImageMinimumSourceSize { \n\ |
47 | +var kMascotImageCode = 'Image { \n\ |
48 | id: mascotImage; \n\ |
49 | objectName: "mascotImage"; \n\ |
50 | anchors { %1 } \n\ |
51 | - source: cardData && cardData["mascot"] || %4; \n\ |
52 | + source: "image://croppedImageMinimumSize/" + (cardData && cardData["mascot"] || %4); \n\ |
53 | width: units.gu(6); \n\ |
54 | height: units.gu(5.625); \n\ |
55 | horizontalAlignment: Image.AlignHCenter; \n\ |
56 | |
57 | === added file 'plugins/Dash/croppedimageminimumsizeimageprovider.cpp' |
58 | --- plugins/Dash/croppedimageminimumsizeimageprovider.cpp 1970-01-01 00:00:00 +0000 |
59 | +++ plugins/Dash/croppedimageminimumsizeimageprovider.cpp 2016-07-15 11:14:48 +0000 |
60 | @@ -0,0 +1,294 @@ |
61 | +/* |
62 | + * Copyright (C) 2016 Canonical, Ltd. |
63 | + * |
64 | + * This program is free software: you can redistribute it and/or modify it under |
65 | + * the terms of the GNU Lesser General Public License version 3, as published by |
66 | + * the Free Software Foundation. |
67 | + * |
68 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
69 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
70 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
71 | + * Lesser General Public License for more details. |
72 | + * |
73 | + * You should have received a copy of the GNU Lesser General Public License |
74 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
75 | + */ |
76 | + |
77 | +#include "croppedimageminimumsizeimageprovider.h" |
78 | + |
79 | +#include <QBuffer> |
80 | +#include <QDebug> |
81 | +#include <QDir> |
82 | +#include <QImageReader> |
83 | +#include <QNetworkReply> |
84 | +#include <QNetworkRequest> |
85 | + |
86 | +#include <QtGui/private/qguiapplication_p.h> |
87 | +#include <qpa/qplatformintegration.h> |
88 | + |
89 | +class AsyncImageResponse : public QQuickImageResponse, public QRunnable |
90 | +{ |
91 | + public: |
92 | + AsyncImageResponse(const QString &id, const QSize &requestedSize, QQmlEngine *engine) |
93 | + : m_id(id), m_requestedSize(requestedSize), m_engine(engine) |
94 | + { |
95 | + setAutoDelete(false); |
96 | + } |
97 | + |
98 | + QQuickTextureFactory *textureFactory() const override |
99 | + { |
100 | + return QQuickTextureFactory::textureFactoryForImage(m_image); |
101 | + } |
102 | + |
103 | + void readImage(const QUrl &u, QIODevice *dev) |
104 | + { |
105 | + QImageReader imgio(dev); |
106 | + |
107 | + if (m_requestedSize.width() > 0 && m_requestedSize.height() > 0) { |
108 | + QSize s = imgio.size(); |
109 | + const qreal imageRatio = s.width() / qreal(s.height()); |
110 | + const qreal requestedRatio = m_requestedSize.width() / qreal(m_requestedSize.height()); |
111 | + qreal ratio; |
112 | + |
113 | + if (requestedRatio > imageRatio) { |
114 | + ratio = m_requestedSize.width() / qreal(s.width()); |
115 | + } else { |
116 | + ratio = m_requestedSize.height() / qreal(s.height()); |
117 | + } |
118 | + |
119 | + qDebug() << "Original size" << s; |
120 | + |
121 | + s.setHeight(qRound(s.height() * ratio)); |
122 | + s.setWidth(qRound(s.width() * ratio)); |
123 | + imgio.setScaledSize(s); |
124 | + } |
125 | + |
126 | + if (!imgio.read(&m_image)) { |
127 | + m_errorString = QString("Error decoding: %1: %2").arg(u.toString()).arg(imgio.errorString()); |
128 | + } |
129 | + |
130 | + qDebug() << "ASYNCRESPONSE" << m_image; |
131 | + } |
132 | + |
133 | + void run() override |
134 | + { |
135 | + const QUrl u = QUrl::fromUserInput(m_id, QDir::currentPath()); |
136 | + |
137 | + qDebug() << "run" << u << u.isLocalFile(); |
138 | + |
139 | + if (u.isLocalFile()) { |
140 | + QFile f(u.toLocalFile()); |
141 | + if (f.open(QIODevice::ReadOnly)) { |
142 | + readImage(u, &f); |
143 | + } else { |
144 | + m_errorString = QString("Cannot open: %1").arg(u.toString()); |
145 | + } |
146 | + emit finished(); |
147 | + } else if (u.scheme() == QLatin1String("image")) { |
148 | + // Daisy chain :/ |
149 | + QQuickImageProvider *provider = dynamic_cast<QQuickImageProvider *>(m_engine->imageProvider(u.host())); |
150 | + |
151 | + if (!provider) { |
152 | + m_errorString = QString("No image provider for: %1").arg(u.toString()); |
153 | + emit finished(); |
154 | + return; |
155 | + } |
156 | + |
157 | + const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps); |
158 | + const QQuickImageProvider::ImageType imageType = provider->imageType(); |
159 | + if (!threadedPixmaps && imageType == QQuickImageProvider::Pixmap) { |
160 | + m_errorString = QString("Can not daisy chain a pixmap provider, platform doesn't support threaded pixmaps: %1").arg(u.toString()); |
161 | + emit finished(); |
162 | + return; |
163 | + } |
164 | + |
165 | + const QString imageId = u.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); |
166 | + |
167 | + switch (imageType) { |
168 | + case QQuickImageProvider::Invalid: |
169 | + { |
170 | + m_errorString = QString("Invalid image provider: %1").arg(u.toString()); |
171 | + break; |
172 | + } |
173 | + |
174 | + case QQuickImageProvider::Image: |
175 | + { |
176 | + QSize readSize; |
177 | + const QSize heightRequest = QSize(0, m_requestedSize.height()); |
178 | + m_image = provider->requestImage(imageId, &readSize, heightRequest); |
179 | + |
180 | + if ((readSize.height() > 0) && |
181 | + (m_requestedSize.height() > 0) && |
182 | + ((readSize.width() / readSize.height()) < (m_requestedSize.width() / m_requestedSize.height()))) |
183 | + { |
184 | + qDebug() << "DOUBLE REQUEST :/" << u; |
185 | + // Doing double request :/ |
186 | + const QSize widthRequest = QSize(m_requestedSize.width(), 0); |
187 | + m_image = provider->requestImage(imageId, &readSize, widthRequest); |
188 | + } |
189 | + |
190 | + if (m_image.isNull()) { |
191 | + m_errorString = QString("Failed to get image from provider: %1").arg(u.toString()); |
192 | + } |
193 | + emit finished(); |
194 | + break; |
195 | + } |
196 | + |
197 | + case QQuickImageProvider::Pixmap: |
198 | + { |
199 | + QSize readSize; |
200 | + const QSize heightRequest = QSize(0, m_requestedSize.height()); |
201 | + QPixmap pixmap = provider->requestPixmap(imageId, &readSize, heightRequest); |
202 | + |
203 | + if ((readSize.height() > 0) && |
204 | + (m_requestedSize.height() > 0) && |
205 | + ((readSize.width() / readSize.height()) < (m_requestedSize.width() / m_requestedSize.height()))) |
206 | + { |
207 | + qDebug() << "DOUBLE REQUEST :/" << u; |
208 | + // Doing double request :/ |
209 | + const QSize widthRequest = QSize(m_requestedSize.width(), 0); |
210 | + pixmap = provider->requestPixmap(imageId, &readSize, widthRequest); |
211 | + } |
212 | + |
213 | + m_image = pixmap.toImage(); |
214 | + |
215 | + if (m_image.isNull()) { |
216 | + m_errorString = QString("Failed to get image from provider: %1").arg(u.toString()); |
217 | + } |
218 | + |
219 | + emit finished(); |
220 | + break; |
221 | + } |
222 | + |
223 | + case QQuickImageProvider::Texture: |
224 | + { |
225 | + QSize readSize; |
226 | + const QSize heightRequest = QSize(0, m_requestedSize.height()); |
227 | + QQuickTextureFactory *t = provider->requestTexture(imageId, &readSize, heightRequest); |
228 | + |
229 | + if ((readSize.height() > 0) && |
230 | + (m_requestedSize.height() > 0) && |
231 | + ((readSize.width() / readSize.height()) < (m_requestedSize.width() / m_requestedSize.height()))) |
232 | + { |
233 | + delete t; |
234 | + qDebug() << "DOUBLE REQUEST :/" << u; |
235 | + // Doing double request :/ |
236 | + const QSize widthRequest = QSize(m_requestedSize.width(), 0); |
237 | + t = provider->requestTexture(imageId, &readSize, widthRequest); |
238 | + } |
239 | + |
240 | + if (!t) { |
241 | + m_errorString = QString("Failed to get texture from provider: %1").arg(u.toString()); |
242 | + } |
243 | + |
244 | + // TODO Do something with t |
245 | + break; |
246 | + } |
247 | + |
248 | + case QQuickImageProvider::ImageResponse: |
249 | + { |
250 | + QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider); |
251 | + const QSize heightRequest = QSize(0, m_requestedSize.height()); |
252 | + QQuickImageResponse *response = asyncProvider->requestImageResponse(imageId, heightRequest); |
253 | + |
254 | + connect(response, &QQuickImageResponse::finished, this, &AsyncImageResponse::asyncResponseFinished); |
255 | + |
256 | + m_eventLoop = new QEventLoop(); |
257 | + m_eventLoop->exec(); |
258 | + delete m_eventLoop; |
259 | + m_eventLoop = 0; |
260 | + |
261 | + // TODO Do the double request magic |
262 | + // TODO Do something with response::textureFactory |
263 | + |
264 | + |
265 | + |
266 | + delete response; |
267 | + |
268 | + break; |
269 | + } |
270 | + } |
271 | + |
272 | + } else { |
273 | + QNetworkAccessManager nam; // TODO only one of these? |
274 | + |
275 | + int redirectCount = 0; |
276 | + |
277 | + QUrl imageUrl = u; |
278 | + bool needToGet = true; |
279 | + while (needToGet && redirectCount < 16) { |
280 | + QNetworkRequest req(imageUrl); |
281 | + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); |
282 | + QNetworkReply *reply = nam.get(req); |
283 | + connect(reply, &QNetworkReply::finished, this, &AsyncImageResponse::asyncResponseFinished); |
284 | + |
285 | + m_eventLoop = new QEventLoop(); |
286 | + m_eventLoop->exec(); |
287 | + delete m_eventLoop; |
288 | + m_eventLoop = 0; |
289 | + |
290 | + const QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); |
291 | + if (redirect.isValid()) { |
292 | + imageUrl = reply->url().resolved(redirect.toUrl()); |
293 | + } else { |
294 | + needToGet = false; |
295 | + |
296 | + if (reply->error()) { |
297 | + m_errorString = reply->errorString(); |
298 | + } else { |
299 | + QByteArray all = reply->readAll(); |
300 | + QBuffer buff(&all); |
301 | + buff.open(QIODevice::ReadOnly); |
302 | + readImage(reply->url(), &buff); |
303 | + } |
304 | + } |
305 | + delete reply; |
306 | + } |
307 | + |
308 | + emit finished(); |
309 | + } |
310 | + } |
311 | + |
312 | + QString errorString() const override |
313 | + { |
314 | + return m_errorString; |
315 | + } |
316 | + |
317 | + void cancel() override |
318 | + { |
319 | + // TODO for QNAM and ImageResponse |
320 | + } |
321 | + |
322 | + public slots: |
323 | + void asyncResponseFinished() |
324 | + { |
325 | + m_eventLoop->exit(); |
326 | + } |
327 | + |
328 | + |
329 | + private: |
330 | + QString m_id; |
331 | + QSize m_requestedSize; |
332 | + QImage m_image; |
333 | + QString m_errorString; |
334 | + QQmlEngine *m_engine; |
335 | + QEventLoop *m_eventLoop; |
336 | +}; |
337 | + |
338 | + |
339 | +CroppedImageMinimumSizeImageProvider::CroppedImageMinimumSizeImageProvider(QQmlEngine *engine) |
340 | + : QQuickAsyncImageProvider() |
341 | + , m_engine(engine) |
342 | +{ |
343 | +} |
344 | + |
345 | +CroppedImageMinimumSizeImageProvider::~CroppedImageMinimumSizeImageProvider() |
346 | +{ |
347 | +} |
348 | + |
349 | +QQuickImageResponse *CroppedImageMinimumSizeImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) |
350 | +{ |
351 | + AsyncImageResponse *response = new AsyncImageResponse(id, requestedSize, m_engine); |
352 | + m_pool.start(response); |
353 | + return response; |
354 | +} |
355 | |
356 | === added file 'plugins/Dash/croppedimageminimumsizeimageprovider.h' |
357 | --- plugins/Dash/croppedimageminimumsizeimageprovider.h 1970-01-01 00:00:00 +0000 |
358 | +++ plugins/Dash/croppedimageminimumsizeimageprovider.h 2016-07-15 11:14:48 +0000 |
359 | @@ -0,0 +1,36 @@ |
360 | +/* |
361 | + * Copyright (C) 2016 Canonical, Ltd. |
362 | + * |
363 | + * This program is free software: you can redistribute it and/or modify it under |
364 | + * the terms of the GNU Lesser General Public License version 3, as published by |
365 | + * the Free Software Foundation. |
366 | + * |
367 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
368 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
369 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
370 | + * Lesser General Public License for more details. |
371 | + * |
372 | + * You should have received a copy of the GNU Lesser General Public License |
373 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
374 | + */ |
375 | + |
376 | +#ifndef CROPPEDIMAGEMINIMUMSIZEIMAGEPROVIDER_H |
377 | +#define CROPPEDIMAGEMINIMUMSIZEIMAGEPROVIDER_H |
378 | + |
379 | +#include <QQuickImageProvider> |
380 | +#include <QThreadPool> |
381 | + |
382 | +class CroppedImageMinimumSizeImageProvider : public QQuickAsyncImageProvider |
383 | +{ |
384 | +public: |
385 | + CroppedImageMinimumSizeImageProvider(QQmlEngine *engine); |
386 | + virtual ~CroppedImageMinimumSizeImageProvider(); |
387 | + |
388 | + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; |
389 | + |
390 | +private: |
391 | + QThreadPool m_pool; |
392 | + QQmlEngine *m_engine; |
393 | +}; |
394 | + |
395 | +#endif // CURSORIMAGEPROVIDER_H |
396 | |
397 | === modified file 'plugins/Dash/plugin.cpp' |
398 | --- plugins/Dash/plugin.cpp 2016-02-19 08:44:42 +0000 |
399 | +++ plugins/Dash/plugin.cpp 2016-07-15 11:14:48 +0000 |
400 | @@ -17,6 +17,7 @@ |
401 | |
402 | #include "plugin.h" |
403 | |
404 | +#include "croppedimageminimumsizeimageprovider.h" |
405 | #include "horizontaljournal.h" |
406 | #include "listviewwithpageheader.h" |
407 | #include "organicgrid.h" |
408 | @@ -67,4 +68,11 @@ |
409 | qmlRegisterSingletonType<AudioComparer>(uri, 0, 1, "AudioUrlComparer", audio_comparer_singleton_provider); |
410 | } |
411 | |
412 | +void DashPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
413 | +{ |
414 | + QQmlExtensionPlugin::initializeEngine(engine, uri); |
415 | + |
416 | + engine->addImageProvider(QStringLiteral("croppedImageMinimumSize"), new CroppedImageMinimumSizeImageProvider(engine)); |
417 | +} |
418 | + |
419 | #include "plugin.moc" |
420 | |
421 | === modified file 'plugins/Dash/plugin.h' |
422 | --- plugins/Dash/plugin.h 2015-04-30 09:31:51 +0000 |
423 | +++ plugins/Dash/plugin.h 2016-07-15 11:14:48 +0000 |
424 | @@ -28,6 +28,7 @@ |
425 | |
426 | public: |
427 | void registerTypes(const char *uri) override; |
428 | + void initializeEngine(QQmlEngine *engine, const char *uri) override; |
429 | }; |
430 | |
431 | #endif |