1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 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 | #include <qtest.h> |
29 | #include <QTextDocument> |
30 | #include <QTcpServer> |
31 | #include <QTcpSocket> |
32 | #include <QDir> |
33 | |
34 | #include <QtQml/qqmlengine.h> |
35 | #include <QtQml/qqmlcomponent.h> |
36 | #include <QtQuick/qquickview.h> |
37 | #include <private/qquickimage_p.h> |
38 | #include <private/qquickimagebase_p.h> |
39 | #include <private/qquickloader_p.h> |
40 | #include <QtQml/qqmlcontext.h> |
41 | #include <QtQml/qqmlexpression.h> |
42 | #include <QtTest/QSignalSpy> |
43 | #include <QtGui/QPainter> |
44 | #include <QtGui/QImageReader> |
45 | #include <QQuickWindow> |
46 | #include <QQuickView> |
47 | #include <QQuickImageProvider> |
48 | #include <QQmlAbstractUrlInterceptor> |
49 | |
50 | #include "../../shared/util.h" |
51 | #include "../../shared/testhttpserver.h" |
52 | #include "../shared/visualtestutil.h" |
53 | |
54 | // #define DEBUG_WRITE_OUTPUT |
55 | |
56 | using namespace QQuickVisualTestUtil; |
57 | |
58 | Q_DECLARE_METATYPE(QQuickImageBase::Status) |
59 | |
60 | class tst_qquickimage : public QQmlDataTest |
61 | { |
62 | Q_OBJECT |
63 | public: |
64 | tst_qquickimage(); |
65 | |
66 | private slots: |
67 | void initTestCase(); |
68 | void cleanup(); |
69 | void noSource(); |
70 | void imageSource(); |
71 | void imageSource_data(); |
72 | void clearSource(); |
73 | void resized(); |
74 | void preserveAspectRatio(); |
75 | void smooth(); |
76 | void mirror(); |
77 | void svg(); |
78 | void svg_data(); |
79 | void geometry(); |
80 | void geometry_data(); |
81 | void big(); |
82 | void tiling_QTBUG_6716(); |
83 | void tiling_QTBUG_6716_data(); |
84 | void noLoading(); |
85 | void paintedWidthHeight(); |
86 | void sourceSize_QTBUG_14303(); |
87 | void sourceSize_QTBUG_16389(); |
88 | void nullPixmapPaint(); |
89 | void imageCrash_QTBUG_22125(); |
90 | void imageCrash_QTBUG_32513(); |
91 | void sourceSize_data(); |
92 | void sourceSize(); |
93 | void sourceClipRect_data(); |
94 | void sourceClipRect(); |
95 | void progressAndStatusChanges(); |
96 | void sourceSizeChanges(); |
97 | void correctStatus(); |
98 | void highdpi(); |
99 | void highDpiFillModesAndSizes_data(); |
100 | void highDpiFillModesAndSizes(); |
101 | void hugeImages(); |
102 | void urlInterceptor(); |
103 | void multiFrame_data(); |
104 | void multiFrame(); |
105 | void colorSpace(); |
106 | |
107 | private: |
108 | QQmlEngine engine; |
109 | QSGRendererInterface::GraphicsApi graphicsApi = QSGRendererInterface::Unknown; |
110 | }; |
111 | |
112 | tst_qquickimage::tst_qquickimage() |
113 | { |
114 | } |
115 | |
116 | void tst_qquickimage::initTestCase() |
117 | { |
118 | QQmlDataTest::initTestCase(); |
119 | QScopedPointer<QQuickView> window(new QQuickView(0)); |
120 | window->show(); |
121 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
122 | graphicsApi = window->rendererInterface()->graphicsApi(); |
123 | } |
124 | |
125 | void tst_qquickimage::cleanup() |
126 | { |
127 | QQuickWindow window; |
128 | window.releaseResources(); |
129 | engine.clearComponentCache(); |
130 | } |
131 | |
132 | void tst_qquickimage::noSource() |
133 | { |
134 | QString componentStr = "import QtQuick 2.0\nImage { source: \"\" }" ; |
135 | QQmlComponent component(&engine); |
136 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
137 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
138 | QVERIFY(obj != nullptr); |
139 | QCOMPARE(obj->source(), QUrl()); |
140 | QCOMPARE(obj->status(), QQuickImage::Null); |
141 | QCOMPARE(obj->width(), 0.); |
142 | QCOMPARE(obj->height(), 0.); |
143 | QCOMPARE(obj->fillMode(), QQuickImage::Stretch); |
144 | QCOMPARE(obj->progress(), 0.0); |
145 | |
146 | delete obj; |
147 | } |
148 | |
149 | void tst_qquickimage::imageSource_data() |
150 | { |
151 | QTest::addColumn<QString>(name: "source" ); |
152 | QTest::addColumn<double>(name: "width" ); |
153 | QTest::addColumn<double>(name: "height" ); |
154 | QTest::addColumn<bool>(name: "remote" ); |
155 | QTest::addColumn<bool>(name: "async" ); |
156 | QTest::addColumn<bool>(name: "cache" ); |
157 | QTest::addColumn<QString>(name: "error" ); |
158 | |
159 | QTest::newRow(dataTag: "local" ) << testFileUrl(fileName: "colors.png" ).toString() << 120.0 << 120.0 << false << false << true << "" ; |
160 | QTest::newRow(dataTag: "local no cache" ) << testFileUrl(fileName: "colors.png" ).toString() << 120.0 << 120.0 << false << false << false << "" ; |
161 | QTest::newRow(dataTag: "local async" ) << testFileUrl(fileName: "colors1.png" ).toString() << 120.0 << 120.0 << false << true << true << "" ; |
162 | QTest::newRow(dataTag: "local not found" ) << testFileUrl(fileName: "no-such-file.png" ).toString() << 0.0 << 0.0 << false |
163 | << false << true << "<Unknown File>:2:1: QML Image: Cannot open: " + testFileUrl(fileName: "no-such-file.png" ).toString(); |
164 | QTest::newRow(dataTag: "local async not found" ) << testFileUrl(fileName: "no-such-file-1.png" ).toString() << 0.0 << 0.0 << false |
165 | << true << true << "<Unknown File>:2:1: QML Image: Cannot open: " + testFileUrl(fileName: "no-such-file-1.png" ).toString(); |
166 | QTest::newRow(dataTag: "remote" ) << "/colors.png" << 120.0 << 120.0 << true << false << true << "" ; |
167 | QTest::newRow(dataTag: "remote redirected" ) << "/oldcolors.png" << 120.0 << 120.0 << true << false << false << "" ; |
168 | if (QImageReader::supportedImageFormats().contains(t: "svg" )) |
169 | QTest::newRow(dataTag: "remote svg" ) << "/heart.svg" << 595.0 << 841.0 << true << false << false << "" ; |
170 | if (QImageReader::supportedImageFormats().contains(t: "svgz" )) |
171 | QTest::newRow(dataTag: "remote svgz" ) << "/heart.svgz" << 595.0 << 841.0 << true << false << false << "" ; |
172 | if (graphicsApi == QSGRendererInterface::OpenGL) { |
173 | QTest::newRow(dataTag: "texturefile pkm format" ) << testFileUrl(fileName: "logo.pkm" ).toString() << 256.0 << 256.0 << false << false << true << "" ; |
174 | QTest::newRow(dataTag: "texturefile ktx format" ) << testFileUrl(fileName: "car.ktx" ).toString() << 146.0 << 80.0 << false << false << true << "" ; |
175 | QTest::newRow(dataTag: "texturefile async" ) << testFileUrl(fileName: "logo.pkm" ).toString() << 256.0 << 256.0 << false << true << true << "" ; |
176 | } |
177 | QTest::newRow(dataTag: "remote not found" ) << "/no-such-file.png" << 0.0 << 0.0 << true |
178 | << false << true << "<Unknown File>:2:1: QML Image: Error transferring {{ServerBaseUrl}}/no-such-file.png - server replied: Not found" ; |
179 | QTest::newRow(dataTag: "extless" ) << testFileUrl(fileName: "colors" ).toString() << 120.0 << 120.0 << false << false << true << "" ; |
180 | QTest::newRow(dataTag: "extless no cache" ) << testFileUrl(fileName: "colors" ).toString() << 120.0 << 120.0 << false << false << false << "" ; |
181 | QTest::newRow(dataTag: "extless async" ) << testFileUrl(fileName: "colors1" ).toString() << 120.0 << 120.0 << false << true << true << "" ; |
182 | QTest::newRow(dataTag: "extless not found" ) << testFileUrl(fileName: "no-such-file" ).toString() << 0.0 << 0.0 << false |
183 | << false << true << "<Unknown File>:2:1: QML Image: Cannot open: " + testFileUrl(fileName: "no-such-file" ).toString(); |
184 | // Test that texture file is preferred over image file, when supported. |
185 | // Since pattern.pkm has different size than pattern.png, these tests verify that the right file has been loaded |
186 | if (graphicsApi == QSGRendererInterface::OpenGL) { |
187 | QTest::newRow(dataTag: "extless prefer-tex" ) << testFileUrl(fileName: "pattern" ).toString() << 64.0 << 64.0 << false << false << true << "" ; |
188 | QTest::newRow(dataTag: "extless prefer-tex async" ) << testFileUrl(fileName: "pattern" ).toString() << 64.0 << 64.0 << false << true << true << "" ; |
189 | } else { |
190 | QTest::newRow(dataTag: "extless ignore-tex" ) << testFileUrl(fileName: "pattern" ).toString() << 200.0 << 200.0 << false << false << true << "" ; |
191 | QTest::newRow(dataTag: "extless ignore-tex async" ) << testFileUrl(fileName: "pattern" ).toString() << 200.0 << 200.0 << false << true << true << "" ; |
192 | } |
193 | |
194 | } |
195 | |
196 | void tst_qquickimage::imageSource() |
197 | { |
198 | QFETCH(QString, source); |
199 | QFETCH(double, width); |
200 | QFETCH(double, height); |
201 | QFETCH(bool, remote); |
202 | QFETCH(bool, async); |
203 | QFETCH(bool, cache); |
204 | QFETCH(QString, error); |
205 | |
206 | |
207 | #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) |
208 | if (qstrcmp(str1: QTest::currentDataTag(), str2: "remote" ) == 0 |
209 | || qstrcmp(str1: QTest::currentDataTag(), str2: "remote redirected" ) == 0 |
210 | || qstrcmp(str1: QTest::currentDataTag(), str2: "remote svg" ) == 0 |
211 | || qstrcmp(str1: QTest::currentDataTag(), str2: "remote svgz" ) == 0 |
212 | || qstrcmp(str1: QTest::currentDataTag(), str2: "remote not found" ) == 0 |
213 | ) { |
214 | QSKIP("Remote tests cause occasional hangs in the CI system -- QTBUG-45655" ); |
215 | } |
216 | #endif |
217 | |
218 | TestHTTPServer server; |
219 | if (remote) { |
220 | QVERIFY2(server.listen(), qPrintable(server.errorString())); |
221 | server.serveDirectory(dataDirectory()); |
222 | server.addRedirect(filename: "oldcolors.png" , redirectName: server.urlString(documentPath: "/colors.png" )); |
223 | source = server.urlString(documentPath: source); |
224 | error.replace(QStringLiteral("{{ServerBaseUrl}}" ), after: server.baseUrl().toString()); |
225 | } |
226 | |
227 | if (!error.isEmpty()) |
228 | QTest::ignoreMessage(type: QtWarningMsg, message: error.toUtf8()); |
229 | |
230 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + source + "\"; asynchronous: " |
231 | + (async ? QLatin1String("true" ) : QLatin1String("false" )) + "; cache: " |
232 | + (cache ? QLatin1String("true" ) : QLatin1String("false" )) + " }" ; |
233 | QQmlComponent component(&engine); |
234 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
235 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
236 | QVERIFY(obj != nullptr); |
237 | |
238 | if (async) |
239 | QVERIFY(obj->asynchronous()); |
240 | else |
241 | QVERIFY(!obj->asynchronous()); |
242 | |
243 | if (cache) |
244 | QVERIFY(obj->cache()); |
245 | else |
246 | QVERIFY(!obj->cache()); |
247 | |
248 | if (remote || async) |
249 | QTRY_COMPARE(obj->status(), QQuickImage::Loading); |
250 | |
251 | QCOMPARE(obj->source(), remote ? source : QUrl(source)); |
252 | |
253 | if (error.isEmpty()) { |
254 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
255 | QCOMPARE(obj->width(), qreal(width)); |
256 | QCOMPARE(obj->height(), qreal(height)); |
257 | QCOMPARE(obj->fillMode(), QQuickImage::Stretch); |
258 | QCOMPARE(obj->progress(), 1.0); |
259 | } else { |
260 | QTRY_COMPARE(obj->status(), QQuickImage::Error); |
261 | } |
262 | |
263 | delete obj; |
264 | } |
265 | |
266 | void tst_qquickimage::clearSource() |
267 | { |
268 | QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }" ; |
269 | QQmlContext *ctxt = engine.rootContext(); |
270 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "colors.png" )); |
271 | QQmlComponent component(&engine); |
272 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
273 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
274 | QVERIFY(obj != nullptr); |
275 | QCOMPARE(obj->status(), QQuickImage::Ready); |
276 | QCOMPARE(obj->width(), 120.); |
277 | QCOMPARE(obj->height(), 120.); |
278 | QCOMPARE(obj->progress(), 1.0); |
279 | |
280 | ctxt->setContextProperty("srcImage" , "" ); |
281 | QVERIFY(obj->source().isEmpty()); |
282 | QCOMPARE(obj->status(), QQuickImage::Null); |
283 | QCOMPARE(obj->width(), 0.); |
284 | QCOMPARE(obj->height(), 0.); |
285 | QCOMPARE(obj->progress(), 0.0); |
286 | |
287 | delete obj; |
288 | } |
289 | |
290 | void tst_qquickimage::resized() |
291 | { |
292 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + testFile(fileName: "colors.png" ) + "\"; width: 300; height: 300 }" ; |
293 | QQmlComponent component(&engine); |
294 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
295 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
296 | QVERIFY(obj != nullptr); |
297 | QCOMPARE(obj->width(), 300.); |
298 | QCOMPARE(obj->height(), 300.); |
299 | QCOMPARE(obj->fillMode(), QQuickImage::Stretch); |
300 | delete obj; |
301 | } |
302 | |
303 | |
304 | void tst_qquickimage::preserveAspectRatio() |
305 | { |
306 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
307 | window->show(); |
308 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
309 | |
310 | window->setSource(testFileUrl(fileName: "aspectratio.qml" )); |
311 | QQuickImage *image = qobject_cast<QQuickImage*>(object: window->rootObject()); |
312 | QVERIFY(image != nullptr); |
313 | image->setWidth(80.0); |
314 | QCOMPARE(image->width(), 80.); |
315 | QCOMPARE(image->height(), 80.); |
316 | |
317 | window->setSource(testFileUrl(fileName: "aspectratio.qml" )); |
318 | image = qobject_cast<QQuickImage*>(object: window->rootObject()); |
319 | image->setHeight(60.0); |
320 | QVERIFY(image != nullptr); |
321 | QCOMPARE(image->height(), 60.); |
322 | QCOMPARE(image->width(), 60.); |
323 | } |
324 | |
325 | void tst_qquickimage::smooth() |
326 | { |
327 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + testFile(fileName: "colors.png" ) + "\"; smooth: true; width: 300; height: 300 }" ; |
328 | QQmlComponent component(&engine); |
329 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
330 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
331 | QVERIFY(obj != nullptr); |
332 | QCOMPARE(obj->width(), 300.); |
333 | QCOMPARE(obj->height(), 300.); |
334 | QCOMPARE(obj->smooth(), true); |
335 | QCOMPARE(obj->fillMode(), QQuickImage::Stretch); |
336 | |
337 | delete obj; |
338 | } |
339 | |
340 | void tst_qquickimage::mirror() |
341 | { |
342 | if ((QGuiApplication::platformName() == QLatin1String("offscreen" )) |
343 | || (QGuiApplication::platformName() == QLatin1String("minimal" ))) |
344 | QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms" ); |
345 | |
346 | QMap<QQuickImage::FillMode, QImage> screenshots; |
347 | QList<QQuickImage::FillMode> fillModes; |
348 | fillModes << QQuickImage::Stretch << QQuickImage::PreserveAspectFit << QQuickImage::PreserveAspectCrop |
349 | << QQuickImage::Tile << QQuickImage::TileVertically << QQuickImage::TileHorizontally << QQuickImage::Pad; |
350 | |
351 | qreal width = 300; |
352 | qreal height = 250; |
353 | qreal devicePixelRatio = 1.0; |
354 | |
355 | foreach (QQuickImage::FillMode fillMode, fillModes) { |
356 | QScopedPointer<QQuickView> window(new QQuickView); |
357 | window->setSource(testFileUrl(fileName: "mirror.qml" )); |
358 | |
359 | QQuickImage *obj = window->rootObject()->findChild<QQuickImage*>(aName: "image" ); |
360 | QVERIFY(obj != nullptr); |
361 | |
362 | obj->setFillMode(fillMode); |
363 | obj->setProperty(name: "mirror" , value: true); |
364 | window->showNormal(); |
365 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
366 | |
367 | QImage screenshot = window->grabWindow(); |
368 | screenshots[fillMode] = screenshot; |
369 | devicePixelRatio = window->devicePixelRatio(); |
370 | } |
371 | |
372 | foreach (QQuickImage::FillMode fillMode, fillModes) { |
373 | QPixmap srcPixmap; |
374 | QVERIFY(srcPixmap.load(testFile("pattern.png" ))); |
375 | |
376 | QPixmap expected(width * (int)devicePixelRatio, height * (int)devicePixelRatio); |
377 | expected.setDevicePixelRatio(devicePixelRatio); |
378 | expected.fill(); |
379 | QPainter p_e(&expected); |
380 | QTransform transform; |
381 | transform.translate(dx: width, dy: 0).scale(sx: -1, sy: 1.0); |
382 | p_e.setTransform(transform); |
383 | |
384 | QPoint offset(width / 2 - srcPixmap.width() / 2, height / 2 - srcPixmap.height() / 2); |
385 | |
386 | switch (fillMode) { |
387 | case QQuickImage::Stretch: |
388 | p_e.drawPixmap(targetRect: QRect(0, 0, width, height), pixmap: srcPixmap, sourceRect: QRect(0, 0, srcPixmap.width(), srcPixmap.height())); |
389 | break; |
390 | case QQuickImage::PreserveAspectFit: |
391 | p_e.drawPixmap(targetRect: QRect(25, 0, height, height), pixmap: srcPixmap, sourceRect: QRect(0, 0, srcPixmap.width(), srcPixmap.height())); |
392 | break; |
393 | case QQuickImage::PreserveAspectCrop: |
394 | { |
395 | qreal ratio = width/srcPixmap.width(); // width is the longer side |
396 | QRect rect(0, 0, srcPixmap.width()*ratio, srcPixmap.height()*ratio); |
397 | rect.moveCenter(p: QRect(0, 0, width, height).center()); |
398 | p_e.drawPixmap(targetRect: rect, pixmap: srcPixmap, sourceRect: QRect(0, 0, srcPixmap.width(), srcPixmap.height())); |
399 | break; |
400 | } |
401 | case QQuickImage::Tile: |
402 | p_e.drawTiledPixmap(rect: QRect(0, 0, width, height), pm: srcPixmap, offset: -offset); |
403 | break; |
404 | case QQuickImage::TileVertically: |
405 | transform.scale(sx: width / srcPixmap.width(), sy: 1.0); |
406 | p_e.setTransform(transform); |
407 | p_e.drawTiledPixmap(rect: QRect(0, 0, width, height), pm: srcPixmap, offset: QPoint(0, -offset.y())); |
408 | break; |
409 | case QQuickImage::TileHorizontally: |
410 | transform.scale(sx: 1.0, sy: height / srcPixmap.height()); |
411 | p_e.setTransform(transform); |
412 | p_e.drawTiledPixmap(rect: QRect(0, 0, width, height), pm: srcPixmap, offset: QPoint(-offset.x(), 0)); |
413 | break; |
414 | case QQuickImage::Pad: |
415 | p_e.drawPixmap(p: offset, pm: srcPixmap); |
416 | break; |
417 | } |
418 | |
419 | QImage img = expected.toImage(); |
420 | QCOMPARE(screenshots[fillMode].convertToFormat(img.format()), img); |
421 | } |
422 | } |
423 | |
424 | void tst_qquickimage::svg_data() |
425 | { |
426 | QTest::addColumn<QString>(name: "src" ); |
427 | QTest::addColumn<QByteArray>(name: "format" ); |
428 | |
429 | QTest::newRow(dataTag: "svg" ) << testFileUrl(fileName: "heart.svg" ).toString() << QByteArray("svg" ); |
430 | QTest::newRow(dataTag: "svgz" ) << testFileUrl(fileName: "heart.svgz" ).toString() << QByteArray("svgz" ); |
431 | } |
432 | |
433 | void tst_qquickimage::svg() |
434 | { |
435 | QFETCH(QString, src); |
436 | QFETCH(QByteArray, format); |
437 | if (!QImageReader::supportedImageFormats().contains(t: format)) |
438 | QSKIP("svg support not available" ); |
439 | |
440 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; sourceSize.width: 300; sourceSize.height: 300 }" ; |
441 | QQmlComponent component(&engine); |
442 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
443 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
444 | QVERIFY(obj != nullptr); |
445 | QCOMPARE(obj->width(), 300.0); |
446 | QCOMPARE(obj->height(), 300.0); |
447 | obj->setSourceSize(QSize(200,200)); |
448 | |
449 | QCOMPARE(obj->width(), 200.0); |
450 | QCOMPARE(obj->height(), 200.0); |
451 | obj->setSourceSize(QSize(100,0)); |
452 | QCOMPARE(obj->width(), 100.0); |
453 | // Due to aspect ratio calculations we can't get a precise |
454 | // check for all setups, so we allow a small margin of error |
455 | QVERIFY(qAbs(obj->height() - 141) < 1); |
456 | |
457 | // Setting it to a size bigger than the actual file, SVG formats |
458 | // can scale up although other image formats cannot |
459 | obj->setSourceSize(QSize(800,0)); |
460 | QCOMPARE(obj->width(), 800.0); |
461 | QVERIFY(qAbs(obj->height() - 1131) < 1); |
462 | delete obj; |
463 | } |
464 | |
465 | void tst_qquickimage::geometry_data() |
466 | { |
467 | QTest::addColumn<QString>(name: "fillMode" ); |
468 | QTest::addColumn<bool>(name: "explicitWidth" ); |
469 | QTest::addColumn<bool>(name: "explicitHeight" ); |
470 | QTest::addColumn<double>(name: "itemWidth" ); |
471 | QTest::addColumn<double>(name: "paintedWidth" ); |
472 | QTest::addColumn<double>(name: "boundingWidth" ); |
473 | QTest::addColumn<double>(name: "itemHeight" ); |
474 | QTest::addColumn<double>(name: "paintedHeight" ); |
475 | QTest::addColumn<double>(name: "boundingHeight" ); |
476 | |
477 | // tested image has width 200, height 100 |
478 | |
479 | // bounding rect and item rect are equal with fillMode PreserveAspectFit, painted rect may be smaller if the aspect ratio doesn't match |
480 | QTest::newRow(dataTag: "PreserveAspectFit" ) << "PreserveAspectFit" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0; |
481 | QTest::newRow(dataTag: "PreserveAspectFit explicit width 300" ) << "PreserveAspectFit" << true << false << 300.0 << 200.0 << 300.0 << 100.0 << 100.0 << 100.0; |
482 | QTest::newRow(dataTag: "PreserveAspectFit explicit height 400" ) << "PreserveAspectFit" << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 100.0 << 400.0; |
483 | QTest::newRow(dataTag: "PreserveAspectFit explicit width 300, height 400" ) << "PreserveAspectFit" << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 150.0 << 400.0; |
484 | |
485 | // bounding rect and painted rect are equal with fillMode PreserveAspectCrop, item rect may be smaller if the aspect ratio doesn't match |
486 | QTest::newRow(dataTag: "PreserveAspectCrop" ) << "PreserveAspectCrop" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0; |
487 | QTest::newRow(dataTag: "PreserveAspectCrop explicit width 300" ) << "PreserveAspectCrop" << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 150.0 << 150.0; |
488 | QTest::newRow(dataTag: "PreserveAspectCrop explicit height 400" ) << "PreserveAspectCrop" << false << true << 200.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0; |
489 | QTest::newRow(dataTag: "PreserveAspectCrop explicit width 300, height 400" ) << "PreserveAspectCrop" << true << true << 300.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0; |
490 | |
491 | // bounding rect, painted rect and item rect are equal in stretching and tiling images |
492 | QStringList fillModes; |
493 | fillModes << "Stretch" << "Tile" << "TileVertically" << "TileHorizontally" ; |
494 | foreach (QString fillMode, fillModes) { |
495 | QTest::newRow(dataTag: fillMode.toLatin1()) << fillMode << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0; |
496 | QTest::newRow(dataTag: QString(fillMode + " explicit width 300" ).toLatin1()) << fillMode << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 100.0 << 100.0; |
497 | QTest::newRow(dataTag: QString(fillMode + " explicit height 400" ).toLatin1()) << fillMode << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 400.0 << 400.0; |
498 | QTest::newRow(dataTag: QString(fillMode + " explicit width 300, height 400" ).toLatin1()) << fillMode << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 400.0 << 400.0; |
499 | } |
500 | } |
501 | |
502 | void tst_qquickimage::geometry() |
503 | { |
504 | QFETCH(QString, fillMode); |
505 | QFETCH(bool, explicitWidth); |
506 | QFETCH(bool, explicitHeight); |
507 | QFETCH(double, itemWidth); |
508 | QFETCH(double, itemHeight); |
509 | QFETCH(double, paintedWidth); |
510 | QFETCH(double, paintedHeight); |
511 | QFETCH(double, boundingWidth); |
512 | QFETCH(double, boundingHeight); |
513 | |
514 | QString src = testFileUrl(fileName: "rect.png" ).toString(); |
515 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; fillMode: Image." + fillMode + "; " ; |
516 | |
517 | if (explicitWidth) |
518 | componentStr.append(s: "width: 300; " ); |
519 | if (explicitHeight) |
520 | componentStr.append(s: "height: 400; " ); |
521 | componentStr.append(s: "}" ); |
522 | QQmlComponent component(&engine); |
523 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
524 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
525 | QVERIFY(obj != nullptr); |
526 | |
527 | QCOMPARE(obj->width(), itemWidth); |
528 | QCOMPARE(obj->paintedWidth(), paintedWidth); |
529 | QCOMPARE(obj->boundingRect().width(), boundingWidth); |
530 | |
531 | QCOMPARE(obj->height(), itemHeight); |
532 | QCOMPARE(obj->paintedHeight(), paintedHeight); |
533 | QCOMPARE(obj->boundingRect().height(), boundingHeight); |
534 | delete obj; |
535 | } |
536 | |
537 | void tst_qquickimage::big() |
538 | { |
539 | // If the JPEG loader does not implement scaling efficiently, it would |
540 | // have to build a 400 MB image. That would be a bug in the JPEG loader. |
541 | |
542 | QString src = testFileUrl(fileName: "big.jpeg" ).toString(); |
543 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 100; sourceSize.height: 256 }" ; |
544 | |
545 | QQmlComponent component(&engine); |
546 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
547 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
548 | QVERIFY(obj != nullptr); |
549 | QCOMPARE(obj->width(), 100.0); |
550 | QCOMPARE(obj->height(), 256.0); |
551 | |
552 | delete obj; |
553 | } |
554 | |
555 | void tst_qquickimage::tiling_QTBUG_6716() |
556 | { |
557 | if ((QGuiApplication::platformName() == QLatin1String("offscreen" )) |
558 | || (QGuiApplication::platformName() == QLatin1String("minimal" ))) |
559 | QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms" ); |
560 | |
561 | QFETCH(QString, source); |
562 | |
563 | QQuickView view(testFileUrl(fileName: source)); |
564 | view.show(); |
565 | QVERIFY(QTest::qWaitForWindowExposed(&view)); |
566 | |
567 | QQuickImage *tiling = findItem<QQuickImage>(parent: view.rootObject(), objectName: "tiling" ); |
568 | |
569 | QVERIFY(tiling != nullptr); |
570 | QImage img = view.grabWindow(); |
571 | for (int x = 0; x < tiling->width(); ++x) { |
572 | for (int y = 0; y < tiling->height(); ++y) { |
573 | QVERIFY(img.pixel(x, y) == qRgb(0, 255, 0)); |
574 | } |
575 | } |
576 | } |
577 | |
578 | void tst_qquickimage::tiling_QTBUG_6716_data() |
579 | { |
580 | QTest::addColumn<QString>(name: "source" ); |
581 | QTest::newRow(dataTag: "vertical_tiling" ) << "vtiling.qml" ; |
582 | QTest::newRow(dataTag: "horizontal_tiling" ) << "htiling.qml" ; |
583 | } |
584 | |
585 | void tst_qquickimage::noLoading() |
586 | { |
587 | qRegisterMetaType<QQuickImageBase::Status>(); |
588 | |
589 | TestHTTPServer server; |
590 | QVERIFY2(server.listen(), qPrintable(server.errorString())); |
591 | server.serveDirectory(dataDirectory()); |
592 | server.addRedirect(filename: "oldcolors.png" , redirectName: server.urlString(documentPath: "/colors.png" )); |
593 | |
594 | QString componentStr = "import QtQuick 2.0\nImage { source: srcImage; cache: true }" ; |
595 | QQmlContext *ctxt = engine.rootContext(); |
596 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart.png" )); |
597 | QQmlComponent component(&engine); |
598 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
599 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
600 | QVERIFY(obj != nullptr); |
601 | QCOMPARE(obj->status(), QQuickImage::Ready); |
602 | |
603 | QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(QUrl))); |
604 | QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal))); |
605 | QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status))); |
606 | |
607 | // Loading local file |
608 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "green.png" )); |
609 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
610 | QTRY_COMPARE(obj->progress(), 1.0); |
611 | QTRY_COMPARE(sourceSpy.count(), 1); |
612 | QTRY_COMPARE(progressSpy.count(), 0); |
613 | QTRY_COMPARE(statusSpy.count(), 1); |
614 | |
615 | // Loading remote file |
616 | ctxt->setContextProperty("srcImage" , server.url(documentPath: "/rect.png" )); |
617 | QTRY_COMPARE(obj->status(), QQuickImage::Loading); |
618 | QTRY_COMPARE(obj->progress(), 0.0); |
619 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
620 | QTRY_COMPARE(obj->progress(), 1.0); |
621 | QTRY_COMPARE(sourceSpy.count(), 2); |
622 | QTRY_VERIFY(progressSpy.count() >= 2); |
623 | QTRY_COMPARE(statusSpy.count(), 3); |
624 | |
625 | // Loading remote file again - should not go through 'Loading' state. |
626 | progressSpy.clear(); |
627 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "green.png" )); |
628 | ctxt->setContextProperty("srcImage" , server.url(documentPath: "/rect.png" )); |
629 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
630 | QTRY_COMPARE(obj->progress(), 1.0); |
631 | QTRY_COMPARE(sourceSpy.count(), 4); |
632 | QTRY_COMPARE(progressSpy.count(), 0); |
633 | QTRY_COMPARE(statusSpy.count(), 5); |
634 | |
635 | delete obj; |
636 | } |
637 | |
638 | void tst_qquickimage::paintedWidthHeight() |
639 | { |
640 | { |
641 | QString src = testFileUrl(fileName: "heart.png" ).toString(); |
642 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 200; height: 25; fillMode: Image.PreserveAspectFit }" ; |
643 | |
644 | QQmlComponent component(&engine); |
645 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
646 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
647 | QVERIFY(obj != nullptr); |
648 | QCOMPARE(obj->width(), 200.0); |
649 | QCOMPARE(obj->height(), 25.0); |
650 | QCOMPARE(obj->paintedWidth(), 25.0); |
651 | QCOMPARE(obj->paintedHeight(), 25.0); |
652 | |
653 | delete obj; |
654 | } |
655 | |
656 | { |
657 | QString src = testFileUrl(fileName: "heart.png" ).toString(); |
658 | QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 26; height: 175; fillMode: Image.PreserveAspectFit }" ; |
659 | QQmlComponent component(&engine); |
660 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
661 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
662 | QVERIFY(obj != nullptr); |
663 | QCOMPARE(obj->width(), 26.0); |
664 | QCOMPARE(obj->height(), 175.0); |
665 | QCOMPARE(obj->paintedWidth(), 26.0); |
666 | QCOMPARE(obj->paintedHeight(), 26.0); |
667 | |
668 | delete obj; |
669 | } |
670 | } |
671 | |
672 | void tst_qquickimage::sourceSize_QTBUG_14303() |
673 | { |
674 | QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }" ; |
675 | QQmlContext *ctxt = engine.rootContext(); |
676 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart200.png" )); |
677 | QQmlComponent component(&engine); |
678 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
679 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
680 | |
681 | QSignalSpy sourceSizeSpy(obj, SIGNAL(sourceSizeChanged())); |
682 | |
683 | QTRY_VERIFY(obj != nullptr); |
684 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
685 | |
686 | QTRY_COMPARE(obj->sourceSize().width(), 200); |
687 | QTRY_COMPARE(obj->sourceSize().height(), 200); |
688 | QTRY_COMPARE(sourceSizeSpy.count(), 0); |
689 | |
690 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "colors.png" )); |
691 | QTRY_COMPARE(obj->sourceSize().width(), 120); |
692 | QTRY_COMPARE(obj->sourceSize().height(), 120); |
693 | QTRY_COMPARE(sourceSizeSpy.count(), 1); |
694 | |
695 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart200.png" )); |
696 | QTRY_COMPARE(obj->sourceSize().width(), 200); |
697 | QTRY_COMPARE(obj->sourceSize().height(), 200); |
698 | QTRY_COMPARE(sourceSizeSpy.count(), 2); |
699 | |
700 | delete obj; |
701 | } |
702 | |
703 | void tst_qquickimage::sourceSize_QTBUG_16389() |
704 | { |
705 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
706 | window->setSource(testFileUrl(fileName: "qtbug_16389.qml" )); |
707 | window->show(); |
708 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
709 | |
710 | QQuickImage *image = findItem<QQuickImage>(parent: window->rootObject(), objectName: "iconImage" ); |
711 | QQuickItem *handle = findItem<QQuickItem>(parent: window->rootObject(), objectName: "blueHandle" ); |
712 | |
713 | QCOMPARE(image->sourceSize().width(), 200); |
714 | QCOMPARE(image->sourceSize().height(), 200); |
715 | QCOMPARE(image->paintedWidth(), 0.0); |
716 | QCOMPARE(image->paintedHeight(), 0.0); |
717 | |
718 | handle->setY(20); |
719 | |
720 | QCOMPARE(image->sourceSize().width(), 200); |
721 | QCOMPARE(image->sourceSize().height(), 200); |
722 | QCOMPARE(image->paintedWidth(), 20.0); |
723 | QCOMPARE(image->paintedHeight(), 20.0); |
724 | } |
725 | |
726 | // QTBUG-15690 |
727 | void tst_qquickimage::nullPixmapPaint() |
728 | { |
729 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
730 | window->setSource(testFileUrl(fileName: "nullpixmap.qml" )); |
731 | window->show(); |
732 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
733 | |
734 | TestHTTPServer server; |
735 | QVERIFY2(server.listen(), qPrintable(server.errorString())); |
736 | server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); |
737 | |
738 | QQuickImage *image = qobject_cast<QQuickImage*>(object: window->rootObject()); |
739 | QTRY_VERIFY(image != nullptr); |
740 | image->setSource(server.url(documentPath: "/no-such-file.png" )); |
741 | |
742 | QQmlTestMessageHandler messageHandler; |
743 | // used to print "QTransform::translate with NaN called" |
744 | QPixmap pm = QPixmap::fromImage(image: window->grabWindow()); |
745 | QVERIFY2(messageHandler.messages().size() == 0, qPrintable(messageHandler.messageString())); |
746 | delete image; |
747 | } |
748 | |
749 | void tst_qquickimage::imageCrash_QTBUG_22125() |
750 | { |
751 | TestHTTPServer server; |
752 | QVERIFY2(server.listen(), qPrintable(server.errorString())); |
753 | server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); |
754 | |
755 | { |
756 | QQuickView view; |
757 | view.rootContext()->setContextProperty(QStringLiteral("serverBaseUrl" ), server.baseUrl()); |
758 | view.setSource(testFileUrl(fileName: "qtbug_22125.qml" )); |
759 | view.show(); |
760 | QVERIFY(QTest::qWaitForWindowExposed(&view)); |
761 | qApp->processEvents(); |
762 | // shouldn't crash when the view drops out of scope due to |
763 | // QQuickPixmapData attempting to dereference a pointer to |
764 | // the destroyed reader. |
765 | } |
766 | |
767 | // shouldn't crash when deleting cancelled QQmlPixmapReplys. |
768 | server.sendDelayedItem(); |
769 | QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); |
770 | QCoreApplication::processEvents(); |
771 | } |
772 | |
773 | void tst_qquickimage::imageCrash_QTBUG_32513() |
774 | { |
775 | QQuickView view(testFileUrl(fileName: "qtbug_32513.qml" )); |
776 | view.show(); |
777 | QVERIFY(QTest::qWaitForWindowExposed(&view)); |
778 | QTest::qWait(ms: 1000); |
779 | // shouldn't crash when the image changes sources |
780 | } |
781 | |
782 | void tst_qquickimage::sourceSize_data() |
783 | { |
784 | QTest::addColumn<int>(name: "sourceWidth" ); |
785 | QTest::addColumn<int>(name: "sourceHeight" ); |
786 | QTest::addColumn<qreal>(name: "implicitWidth" ); |
787 | QTest::addColumn<qreal>(name: "implicitHeight" ); |
788 | |
789 | QTest::newRow(dataTag: "unscaled" ) << 0 << 0 << 300.0 << 300.0; |
790 | QTest::newRow(dataTag: "scale width" ) << 100 << 0 << 100.0 << 100.0; |
791 | QTest::newRow(dataTag: "scale height" ) << 0 << 150 << 150.0 << 150.0; |
792 | QTest::newRow(dataTag: "larger sourceSize" ) << 400 << 400 << 300.0 << 300.0; |
793 | } |
794 | |
795 | void tst_qquickimage::sourceSize() |
796 | { |
797 | QFETCH(int, sourceWidth); |
798 | QFETCH(int, sourceHeight); |
799 | QFETCH(qreal, implicitWidth); |
800 | QFETCH(qreal, implicitHeight); |
801 | |
802 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
803 | QQmlContext *ctxt = window->rootContext(); |
804 | ctxt->setContextProperty("srcWidth" , sourceWidth); |
805 | ctxt->setContextProperty("srcHeight" , sourceHeight); |
806 | |
807 | window->setSource(testFileUrl(fileName: "sourceSize.qml" )); |
808 | window->show(); |
809 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
810 | |
811 | QQuickImage *image = qobject_cast<QQuickImage*>(object: window->rootObject()); |
812 | QVERIFY(image); |
813 | |
814 | QCOMPARE(image->sourceSize().width(), sourceWidth); |
815 | QCOMPARE(image->sourceSize().height(), sourceHeight); |
816 | QCOMPARE(image->implicitWidth(), implicitWidth); |
817 | QCOMPARE(image->implicitHeight(), implicitHeight); |
818 | } |
819 | |
820 | void tst_qquickimage::sourceSizeChanges() |
821 | { |
822 | TestHTTPServer server; |
823 | QVERIFY2(server.listen(), qPrintable(server.errorString())); |
824 | server.serveDirectory(dataDirectory()); |
825 | |
826 | QQmlEngine engine; |
827 | QQmlComponent component(&engine); |
828 | component.setData("import QtQuick 2.0\nImage { source: srcImage }" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
829 | QTRY_VERIFY(component.isReady()); |
830 | QQmlContext *ctxt = engine.rootContext(); |
831 | ctxt->setContextProperty("srcImage" , "" ); |
832 | QQuickImage *img = qobject_cast<QQuickImage*>(object: component.create()); |
833 | QVERIFY(img != nullptr); |
834 | |
835 | QSignalSpy sourceSizeSpy(img, SIGNAL(sourceSizeChanged())); |
836 | |
837 | // Local |
838 | ctxt->setContextProperty("srcImage" , QUrl("" )); |
839 | QTRY_COMPARE(img->status(), QQuickImage::Null); |
840 | QTRY_COMPARE(sourceSizeSpy.count(), 0); |
841 | |
842 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart.png" )); |
843 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
844 | QTRY_COMPARE(sourceSizeSpy.count(), 1); |
845 | |
846 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart.png" )); |
847 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
848 | QTRY_COMPARE(sourceSizeSpy.count(), 1); |
849 | |
850 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart_copy.png" )); |
851 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
852 | QTRY_COMPARE(sourceSizeSpy.count(), 1); |
853 | |
854 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "colors.png" )); |
855 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
856 | QTRY_COMPARE(sourceSizeSpy.count(), 2); |
857 | |
858 | ctxt->setContextProperty("srcImage" , QUrl("" )); |
859 | QTRY_COMPARE(img->status(), QQuickImage::Null); |
860 | QTRY_COMPARE(sourceSizeSpy.count(), 3); |
861 | |
862 | // Remote |
863 | ctxt->setContextProperty("srcImage" , server.url(documentPath: "/heart.png" )); |
864 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
865 | QTRY_COMPARE(sourceSizeSpy.count(), 4); |
866 | |
867 | ctxt->setContextProperty("srcImage" , server.url(documentPath: "/heart.png" )); |
868 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
869 | QTRY_COMPARE(sourceSizeSpy.count(), 4); |
870 | |
871 | ctxt->setContextProperty("srcImage" , server.url(documentPath: "/heart_copy.png" )); |
872 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
873 | QTRY_COMPARE(sourceSizeSpy.count(), 4); |
874 | |
875 | ctxt->setContextProperty("srcImage" , server.url(documentPath: "/colors.png" )); |
876 | QTRY_COMPARE(img->status(), QQuickImage::Ready); |
877 | QTRY_COMPARE(sourceSizeSpy.count(), 5); |
878 | |
879 | ctxt->setContextProperty("srcImage" , QUrl("" )); |
880 | QTRY_COMPARE(img->status(), QQuickImage::Null); |
881 | QTRY_COMPARE(sourceSizeSpy.count(), 6); |
882 | |
883 | delete img; |
884 | } |
885 | |
886 | void tst_qquickimage::sourceClipRect_data() |
887 | { |
888 | QTest::addColumn<QRectF>(name: "sourceClipRect" ); |
889 | QTest::addColumn<QSize>(name: "sourceSize" ); |
890 | QTest::addColumn<QList<QPoint>>(name: "redPixelLocations" ); |
891 | QTest::addColumn<QList<QPoint>>(name: "bluePixelLocations" ); |
892 | |
893 | QTest::newRow(dataTag: "unclipped" ) << QRectF() << QSize() |
894 | << (QList<QPoint>() << QPoint(80, 80) << QPoint(150, 256)) |
895 | << (QList<QPoint>() << QPoint(28, 28) << QPoint(215, 215)); |
896 | QTest::newRow(dataTag: "upperLeft" ) << QRectF(10, 10, 100, 100) << QSize() |
897 | << (QList<QPoint>() << QPoint(99, 99)) |
898 | << (QList<QPoint>() << QPoint(100, 100) << QPoint(28, 28)); |
899 | QTest::newRow(dataTag: "lowerRight" ) << QRectF(200, 200, 20, 20) << QSize() |
900 | << (QList<QPoint>() << QPoint(0, 0)) |
901 | << (QList<QPoint>() << QPoint(14, 14)); |
902 | QTest::newRow(dataTag: "miniMiddle" ) << QRectF(20, 20, 60, 60) << QSize(100, 100) |
903 | << (QList<QPoint>() << QPoint(59, 0) << QPoint(6, 12) << QPoint(42, 42)) |
904 | << (QList<QPoint>() << QPoint(54, 54) << QPoint(15, 59)); |
905 | } |
906 | |
907 | void tst_qquickimage::sourceClipRect() |
908 | { |
909 | QFETCH(QRectF, sourceClipRect); |
910 | QFETCH(QSize, sourceSize); |
911 | QFETCH(QList<QPoint>, redPixelLocations); |
912 | QFETCH(QList<QPoint>, bluePixelLocations); |
913 | |
914 | QScopedPointer<QQuickView> window(new QQuickView(nullptr)); |
915 | |
916 | window->setColor(Qt::blue); |
917 | window->setSource(testFileUrl(fileName: "image.qml" )); |
918 | window->show(); |
919 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
920 | |
921 | QQuickImage *image = qobject_cast<QQuickImage*>(object: window->rootObject()); |
922 | QVERIFY(image); |
923 | |
924 | image->setSourceSize(sourceSize); |
925 | QCOMPARE(image->implicitWidth(), sourceSize.isValid() ? sourceSize.width() : 300); |
926 | QCOMPARE(image->implicitHeight(), sourceSize.isValid() ? sourceSize.height() : 300); |
927 | image->setSourceClipRect(sourceClipRect); |
928 | QCOMPARE(image->implicitWidth(), sourceClipRect.isNull() ? 300 : sourceClipRect.width()); |
929 | QCOMPARE(image->implicitHeight(), sourceClipRect.isNull() ? 300 : sourceClipRect.height()); |
930 | |
931 | if ((QGuiApplication::platformName() == QLatin1String("offscreen" )) |
932 | || (QGuiApplication::platformName() == QLatin1String("minimal" ))) |
933 | QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms" ); |
934 | QImage contents = window->grabWindow(); |
935 | if (contents.width() < sourceClipRect.width()) |
936 | QSKIP("Skipping due to grabWindow not functional" ); |
937 | #ifdef DEBUG_WRITE_OUTPUT |
938 | contents.save("/tmp/sourceClipRect_" + QLatin1String(QTest::currentDataTag()) + ".png" ); |
939 | #endif |
940 | for (auto p : redPixelLocations) { |
941 | QRgb color = contents.pixel(pt: p); |
942 | QVERIFY(qRed(color) > 0xc0); |
943 | QVERIFY(qBlue(color) < 0x0f); |
944 | } |
945 | for (auto p : bluePixelLocations){ |
946 | QRgb color = contents.pixel(pt: p); |
947 | QVERIFY(qBlue(color) > 0xc0); |
948 | QVERIFY(qRed(color) < 0x0f); |
949 | } |
950 | } |
951 | |
952 | void tst_qquickimage::progressAndStatusChanges() |
953 | { |
954 | TestHTTPServer server; |
955 | QVERIFY2(server.listen(), qPrintable(server.errorString())); |
956 | server.serveDirectory(dataDirectory()); |
957 | |
958 | QQmlEngine engine; |
959 | QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }" ; |
960 | QQmlContext *ctxt = engine.rootContext(); |
961 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart.png" )); |
962 | QQmlComponent component(&engine); |
963 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
964 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
965 | QVERIFY(obj != nullptr); |
966 | QCOMPARE(obj->status(), QQuickImage::Ready); |
967 | QTRY_COMPARE(obj->progress(), 1.0); |
968 | |
969 | qRegisterMetaType<QQuickImageBase::Status>(); |
970 | QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(QUrl))); |
971 | QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal))); |
972 | QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status))); |
973 | |
974 | // Same image |
975 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart.png" )); |
976 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
977 | QTRY_COMPARE(obj->progress(), 1.0); |
978 | QTRY_COMPARE(sourceSpy.count(), 0); |
979 | QTRY_COMPARE(progressSpy.count(), 0); |
980 | QTRY_COMPARE(statusSpy.count(), 0); |
981 | |
982 | // Loading local file |
983 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "colors.png" )); |
984 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
985 | QTRY_COMPARE(obj->progress(), 1.0); |
986 | QTRY_COMPARE(sourceSpy.count(), 1); |
987 | QTRY_COMPARE(progressSpy.count(), 0); |
988 | QTRY_COMPARE(statusSpy.count(), 1); |
989 | |
990 | // Loading remote file |
991 | ctxt->setContextProperty("srcImage" , server.url(documentPath: "/heart.png" )); |
992 | QTRY_COMPARE(obj->status(), QQuickImage::Loading); |
993 | QTRY_COMPARE(obj->progress(), 0.0); |
994 | QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
995 | QTRY_COMPARE(obj->progress(), 1.0); |
996 | QTRY_COMPARE(sourceSpy.count(), 2); |
997 | QTRY_VERIFY(progressSpy.count() > 1); |
998 | QTRY_COMPARE(statusSpy.count(), 3); |
999 | |
1000 | ctxt->setContextProperty("srcImage" , "" ); |
1001 | QTRY_COMPARE(obj->status(), QQuickImage::Null); |
1002 | QTRY_COMPARE(obj->progress(), 0.0); |
1003 | QTRY_COMPARE(sourceSpy.count(), 3); |
1004 | QTRY_VERIFY(progressSpy.count() > 2); |
1005 | QTRY_COMPARE(statusSpy.count(), 4); |
1006 | |
1007 | delete obj; |
1008 | } |
1009 | |
1010 | class TestQImageProvider : public QQuickImageProvider |
1011 | { |
1012 | public: |
1013 | TestQImageProvider() : QQuickImageProvider(Image) {} |
1014 | |
1015 | QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize) |
1016 | { |
1017 | Q_UNUSED(requestedSize); |
1018 | if (id == QLatin1String("first-image.png" )) { |
1019 | QTest::qWait(ms: 50); |
1020 | int width = 100; |
1021 | int height = 100; |
1022 | QImage image(width, height, QImage::Format_RGB32); |
1023 | image.fill(pixel: QColor("yellow" ).rgb()); |
1024 | if (size) |
1025 | *size = QSize(width, height); |
1026 | return image; |
1027 | } |
1028 | |
1029 | QTest::qWait(ms: 400); |
1030 | int width = 100; |
1031 | int height = 100; |
1032 | QImage image(width, height, QImage::Format_RGB32); |
1033 | image.fill(pixel: QColor("green" ).rgb()); |
1034 | if (size) |
1035 | *size = QSize(width, height); |
1036 | return image; |
1037 | } |
1038 | }; |
1039 | |
1040 | void tst_qquickimage::correctStatus() |
1041 | { |
1042 | QQmlEngine engine; |
1043 | engine.addImageProvider(id: QLatin1String("test" ), new TestQImageProvider()); |
1044 | |
1045 | QQmlComponent component(&engine, testFileUrl(fileName: "correctStatus.qml" )); |
1046 | QObject *obj = component.create(); |
1047 | QVERIFY(obj); |
1048 | |
1049 | QTest::qWait(ms: 200); |
1050 | |
1051 | // at this point image1 should be attempting to load second-image.png, |
1052 | // and should be in the loading state. Without a clear prior to that load, |
1053 | // the status can mistakenly be in the ready state. |
1054 | QCOMPARE(obj->property("status" ).toInt(), int(QQuickImage::Loading)); |
1055 | |
1056 | QTest::qWait(ms: 400); |
1057 | delete obj; |
1058 | } |
1059 | |
1060 | void tst_qquickimage::highdpi() |
1061 | { |
1062 | QString componentStr = "import QtQuick 2.0\nImage { source: srcImage ; }" ; |
1063 | QQmlComponent component(&engine); |
1064 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
1065 | QQmlContext *ctxt = engine.rootContext(); |
1066 | |
1067 | // Testing "@2x" high-dpi image loading: |
1068 | // The basic case is as follows. Suppose you have foo.png, |
1069 | // which is a 64x64 png that fits in a QML layout. Now, |
1070 | // on a high-dpi system that pixmap would not provide |
1071 | // enough pixels. To fix this the app developer provides |
1072 | // a 128x128 foo@2x.png, which Qt automatically loads. |
1073 | // The image continues to be referred to as "foo.png" in |
1074 | // the QML sources, and reports a size of 64x64. |
1075 | // |
1076 | |
1077 | // Load "heart-highdpi@2x.png", which is a 300x300 png. As a 2x scale image it |
1078 | // should render and report a geometry of 150x150. |
1079 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart-highdpi@2x.png" )); |
1080 | |
1081 | QQuickImage *obj = qobject_cast<QQuickImage*>(object: component.create()); |
1082 | QVERIFY(obj != nullptr); |
1083 | |
1084 | QCOMPARE(obj->width(), 150.0); |
1085 | QCOMPARE(obj->height(), 150.0); |
1086 | QCOMPARE(obj->paintedWidth(), 150.0); |
1087 | QCOMPARE(obj->paintedHeight(), 150.0); |
1088 | |
1089 | // Load a normal 1x image. |
1090 | ctxt->setContextProperty("srcImage" , testFileUrl(fileName: "heart.png" )); |
1091 | QCOMPARE(obj->width(), 300.0); |
1092 | QCOMPARE(obj->height(), 300.0); |
1093 | QCOMPARE(obj->paintedWidth(), 300.0); |
1094 | QCOMPARE(obj->paintedHeight(), 300.0); |
1095 | |
1096 | delete obj; |
1097 | } |
1098 | |
1099 | void tst_qquickimage::highDpiFillModesAndSizes_data() |
1100 | { |
1101 | QTest::addColumn<QQuickImage::FillMode>(name: "fillMode" ); |
1102 | QTest::addColumn<qreal>(name: "expectedHeightAfterSettingWidthTo100" ); |
1103 | QTest::addColumn<qreal>(name: "expectedImplicitHeightAfterSettingWidthTo100" ); |
1104 | QTest::addColumn<qreal>(name: "expectedPaintedWidthAfterSettingWidthTo100" ); |
1105 | QTest::addColumn<qreal>(name: "expectedPaintedHeightAfterSettingWidthTo100" ); |
1106 | |
1107 | QTest::addRow(format: "Stretch" ) << QQuickImage::Stretch << 150.0 << 150.0 << 100.0 << 150.0; |
1108 | QTest::addRow(format: "PreserveAspectFit" ) << QQuickImage::PreserveAspectFit << 100.0 << 100.0 << 100.0 << 100.0; |
1109 | QTest::addRow(format: "PreserveAspectCrop" ) << QQuickImage::PreserveAspectCrop << 150.0 << 150.0 << 150.0 << 150.0; |
1110 | QTest::addRow(format: "Tile" ) << QQuickImage::Tile << 150.0 << 150.0 << 100.0 << 150.0; |
1111 | QTest::addRow(format: "TileVertically" ) << QQuickImage::TileVertically << 150.0 << 150.0 << 100.0 << 150.0; |
1112 | QTest::addRow(format: "TileHorizontally" ) << QQuickImage::TileHorizontally << 150.0 << 150.0 << 100.0 << 150.0; |
1113 | QTest::addRow(format: "Pad" ) << QQuickImage::Pad << 150.0 << 150.0 << 150.0 << 150.0; |
1114 | } |
1115 | |
1116 | void tst_qquickimage::highDpiFillModesAndSizes() |
1117 | { |
1118 | QFETCH(QQuickImage::FillMode, fillMode); |
1119 | QFETCH(qreal, expectedHeightAfterSettingWidthTo100); |
1120 | QFETCH(qreal, expectedImplicitHeightAfterSettingWidthTo100); |
1121 | QFETCH(qreal, expectedPaintedWidthAfterSettingWidthTo100); |
1122 | QFETCH(qreal, expectedPaintedHeightAfterSettingWidthTo100); |
1123 | |
1124 | QString componentStr = "import QtQuick 2.0\nImage { source: srcImage; }" ; |
1125 | QQmlComponent component(&engine); |
1126 | component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
1127 | |
1128 | engine.rootContext()->setContextProperty("srcImage" , testFileUrl(fileName: "heart-highdpi@2x.png" )); |
1129 | |
1130 | QScopedPointer<QQuickImage> image(qobject_cast<QQuickImage*>(object: component.create())); |
1131 | QVERIFY(image); |
1132 | QCOMPARE(image->width(), 150.0); |
1133 | QCOMPARE(image->height(), 150.0); |
1134 | QCOMPARE(image->paintedWidth(), 150.0); |
1135 | QCOMPARE(image->paintedHeight(), 150.0); |
1136 | QCOMPARE(image->implicitWidth(), 150.0); |
1137 | QCOMPARE(image->implicitHeight(), 150.0); |
1138 | QCOMPARE(image->paintedWidth(), 150.0); |
1139 | QCOMPARE(image->paintedHeight(), 150.0); |
1140 | |
1141 | // The implicit size should not change when setting any fillMode here. |
1142 | image->setFillMode(fillMode); |
1143 | QCOMPARE(image->fillMode(), fillMode); |
1144 | QCOMPARE(image->implicitWidth(), 150.0); |
1145 | QCOMPARE(image->implicitHeight(), 150.0); |
1146 | QCOMPARE(image->paintedWidth(), 150.0); |
1147 | QCOMPARE(image->paintedHeight(), 150.0); |
1148 | |
1149 | image->setWidth(100.0); |
1150 | QCOMPARE(image->width(), 100.0); |
1151 | QCOMPARE(image->height(), expectedHeightAfterSettingWidthTo100); |
1152 | QCOMPARE(image->implicitWidth(), 150.0); |
1153 | QCOMPARE(image->implicitHeight(), expectedImplicitHeightAfterSettingWidthTo100); |
1154 | QCOMPARE(image->paintedWidth(), expectedPaintedWidthAfterSettingWidthTo100); |
1155 | QCOMPARE(image->paintedHeight(), expectedPaintedHeightAfterSettingWidthTo100); |
1156 | } |
1157 | |
1158 | void tst_qquickimage::hugeImages() |
1159 | { |
1160 | if ((QGuiApplication::platformName() == QLatin1String("offscreen" )) |
1161 | || (QGuiApplication::platformName() == QLatin1String("minimal" ))) |
1162 | QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms" ); |
1163 | |
1164 | QQuickView view; |
1165 | view.setSource(testFileUrl(fileName: "hugeImages.qml" )); |
1166 | view.setGeometry(posx: 0, posy: 0, w: 200, h: 200); |
1167 | view.create(); |
1168 | |
1169 | QImage contents = view.grabWindow(); |
1170 | |
1171 | QCOMPARE(contents.pixel(0, 0), qRgba(255, 0, 0, 255)); |
1172 | QCOMPARE(contents.pixel(99, 99), qRgba(255, 0, 0, 255)); |
1173 | QCOMPARE(contents.pixel(100, 0), qRgba(0, 0, 255, 255)); |
1174 | QCOMPARE(contents.pixel(199, 99), qRgba(0, 0, 255, 255)); |
1175 | } |
1176 | |
1177 | |
1178 | class MyInterceptor : public QQmlAbstractUrlInterceptor |
1179 | { |
1180 | public: |
1181 | MyInterceptor(QUrl url) : QQmlAbstractUrlInterceptor(), m_url(url) {} |
1182 | QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType) |
1183 | { |
1184 | if (url.scheme() == "interceptthis" ) |
1185 | return m_url; |
1186 | return url; |
1187 | } |
1188 | |
1189 | QUrl m_url; |
1190 | }; |
1191 | |
1192 | void tst_qquickimage::urlInterceptor() |
1193 | { |
1194 | QQmlEngine engine; |
1195 | MyInterceptor interceptor {testFileUrl(fileName: "colors.png" )}; |
1196 | engine.setUrlInterceptor(&interceptor); |
1197 | |
1198 | QQmlComponent c(&engine); |
1199 | |
1200 | c.setData("import QtQuick 2.12; Image { objectName: \"item\"; source: width == 0 ? \"interceptthis:doesNotExist\" : \"interceptthis:doesNotExist\"}" , baseUrl: QUrl{}); |
1201 | QScopedPointer<QQuickImage> object { qobject_cast<QQuickImage*>(object: c.create())}; |
1202 | QVERIFY(object); |
1203 | QTRY_COMPARE(object->status(), QQuickImage::Ready); |
1204 | QTRY_COMPARE(object->progress(), 1.0); |
1205 | } |
1206 | |
1207 | void tst_qquickimage::multiFrame_data() |
1208 | { |
1209 | QTest::addColumn<QString>(name: "qmlfile" ); |
1210 | QTest::addColumn<bool>(name: "asynchronous" ); |
1211 | |
1212 | QTest::addRow(format: "default" ) << "multiframe.qml" << false; |
1213 | QTest::addRow(format: "async" ) << "multiframeAsync.qml" << true; |
1214 | } |
1215 | |
1216 | void tst_qquickimage::multiFrame() |
1217 | { |
1218 | if ((QGuiApplication::platformName() == QLatin1String("offscreen" )) |
1219 | || (QGuiApplication::platformName() == QLatin1String("minimal" ))) |
1220 | QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms" ); |
1221 | |
1222 | QFETCH(QString, qmlfile); |
1223 | QFETCH(bool, asynchronous); |
1224 | Q_UNUSED(asynchronous) |
1225 | |
1226 | QQuickView view(testFileUrl(fileName: qmlfile)); |
1227 | QQuickImage *image = qobject_cast<QQuickImage*>(object: view.rootObject()); |
1228 | QVERIFY(image); |
1229 | QSignalSpy countSpy(image, SIGNAL(frameCountChanged())); |
1230 | QSignalSpy currentSpy(image, SIGNAL(currentFrameChanged())); |
1231 | if (asynchronous) { |
1232 | QCOMPARE(image->frameCount(), 0); |
1233 | QTRY_COMPARE(image->frameCount(), 4); |
1234 | QCOMPARE(countSpy.count(), 1); |
1235 | } else { |
1236 | QCOMPARE(image->frameCount(), 4); |
1237 | } |
1238 | QCOMPARE(image->currentFrame(), 0); |
1239 | view.show(); |
1240 | QVERIFY(QTest::qWaitForWindowExposed(&view)); |
1241 | |
1242 | QImage contents = view.grabWindow(); |
1243 | if (contents.width() < 40) |
1244 | QSKIP("Skipping due to grabWindow not functional" ); |
1245 | // The first frame is a blue ball, approximately qRgba(0x33, 0x6d, 0xcc, 0xff) |
1246 | QRgb color = contents.pixel(x: 16, y: 16); |
1247 | QVERIFY(qRed(color) < 0xc0); |
1248 | QVERIFY(qGreen(color) < 0xc0); |
1249 | QVERIFY(qBlue(color) > 0xc0); |
1250 | |
1251 | image->setCurrentFrame(1); |
1252 | QTRY_COMPARE(image->status(), QQuickImageBase::Ready); |
1253 | QCOMPARE(currentSpy.count(), 1); |
1254 | QCOMPARE(image->currentFrame(), 1); |
1255 | contents = view.grabWindow(); |
1256 | // The second frame is a green ball, approximately qRgba(0x27, 0xc8, 0x22, 0xff) |
1257 | color = contents.pixel(x: 16, y: 16); |
1258 | QVERIFY(qRed(color) < 0xc0); |
1259 | QVERIFY(qGreen(color) > 0xc0); |
1260 | QVERIFY(qBlue(color) < 0xc0); |
1261 | } |
1262 | |
1263 | void tst_qquickimage::colorSpace() |
1264 | { |
1265 | QString componentStr1 = "import QtQuick 2.15\n" |
1266 | "Image { source: srcImage; }" ; |
1267 | QQmlComponent component1(&engine); |
1268 | component1.setData(componentStr1.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
1269 | engine.rootContext()->setContextProperty("srcImage" , testFileUrl(fileName: "ProPhoto.jpg" )); |
1270 | |
1271 | QScopedPointer<QQuickImage> object1 { qobject_cast<QQuickImage*>(object: component1.create())}; |
1272 | QVERIFY(object1); |
1273 | QTRY_COMPARE(object1->status(), QQuickImageBase::Ready); |
1274 | QCOMPARE(object1->colorSpace(), QColorSpace(QColorSpace::ProPhotoRgb)); |
1275 | |
1276 | QString componentStr2 = "import QtQuick 2.15\n" |
1277 | "Image {\n" |
1278 | " source: srcImage;\n" |
1279 | " colorSpace.namedColorSpace: ColorSpace.SRgb;\n" |
1280 | "}" ; |
1281 | |
1282 | QQmlComponent component2(&engine); |
1283 | component2.setData(componentStr2.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: "" )); |
1284 | |
1285 | QScopedPointer<QQuickImage> object2 { qobject_cast<QQuickImage*>(object: component2.create())}; |
1286 | QVERIFY(object2); |
1287 | QTRY_COMPARE(object2->status(), QQuickImageBase::Ready); |
1288 | QCOMPARE(object2->colorSpace(), QColorSpace(QColorSpace::SRgb)); |
1289 | } |
1290 | |
1291 | QTEST_MAIN(tst_qquickimage) |
1292 | |
1293 | #include "tst_qquickimage.moc" |
1294 | |