/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../shared/util.h" #include "../../shared/testhttpserver.h" #include "../shared/visualtestutil.h" using namespace QQuickVisualTestUtil; Q_DECLARE_METATYPE(QQuickImageBase::Status) class tst_qquickimage : public QQmlDataTest { Q_OBJECT public: tst_qquickimage(); private slots: void cleanup(); void noSource(); void imageSource(); void imageSource_data(); void clearSource(); void resized(); void preserveAspectRatio(); void smooth(); void mirror(); void svg(); void svg_data(); void geometry(); void geometry_data(); void big(); void tiling_QTBUG_6716(); void tiling_QTBUG_6716_data(); void noLoading(); void paintedWidthHeight(); void sourceSize_QTBUG_14303(); void sourceSize_QTBUG_16389(); void nullPixmapPaint(); void imageCrash_QTBUG_22125(); void imageCrash_QTBUG_32513(); void sourceSize_data(); void sourceSize(); void progressAndStatusChanges(); void sourceSizeChanges(); void correctStatus(); void highdpi(); void hugeImages(); private: QQmlEngine engine; }; tst_qquickimage::tst_qquickimage() { } void tst_qquickimage::cleanup() { QQuickWindow window; window.releaseResources(); engine.clearComponentCache(); } void tst_qquickimage::noSource() { QString componentStr = "import QtQuick 2.0\nImage { source: \"\" }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->source(), QUrl()); QCOMPARE(obj->status(), QQuickImage::Null); QCOMPARE(obj->width(), 0.); QCOMPARE(obj->height(), 0.); QCOMPARE(obj->fillMode(), QQuickImage::Stretch); QCOMPARE(obj->progress(), 0.0); delete obj; } void tst_qquickimage::imageSource_data() { QTest::addColumn("source"); QTest::addColumn("width"); QTest::addColumn("height"); QTest::addColumn("remote"); QTest::addColumn("async"); QTest::addColumn("cache"); QTest::addColumn("error"); QTest::newRow("local") << testFileUrl("colors.png").toString() << 120.0 << 120.0 << false << false << true << ""; QTest::newRow("local no cache") << testFileUrl("colors.png").toString() << 120.0 << 120.0 << false << false << false << ""; QTest::newRow("local async") << testFileUrl("colors1.png").toString() << 120.0 << 120.0 << false << true << true << ""; QTest::newRow("local not found") << testFileUrl("no-such-file.png").toString() << 0.0 << 0.0 << false << false << true << ":2:1: QML Image: Cannot open: " + testFileUrl("no-such-file.png").toString(); QTest::newRow("local async not found") << testFileUrl("no-such-file-1.png").toString() << 0.0 << 0.0 << false << true << true << ":2:1: QML Image: Cannot open: " + testFileUrl("no-such-file-1.png").toString(); QTest::newRow("remote") << "/colors.png" << 120.0 << 120.0 << true << false << true << ""; QTest::newRow("remote redirected") << "/oldcolors.png" << 120.0 << 120.0 << true << false << false << ""; if (QImageReader::supportedImageFormats().contains("svg")) QTest::newRow("remote svg") << "/heart.svg" << 550.0 << 500.0 << true << false << false << ""; if (QImageReader::supportedImageFormats().contains("svgz")) QTest::newRow("remote svgz") << "/heart.svgz" << 550.0 << 500.0 << true << false << false << ""; QTest::newRow("remote not found") << "/no-such-file.png" << 0.0 << 0.0 << true << false << true << ":2:1: QML Image: Error transferring {{ServerBaseUrl}}/no-such-file.png - server replied: Not found"; } void tst_qquickimage::imageSource() { QFETCH(QString, source); QFETCH(double, width); QFETCH(double, height); QFETCH(bool, remote); QFETCH(bool, async); QFETCH(bool, cache); QFETCH(QString, error); #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) if (qstrcmp(QTest::currentDataTag(), "remote") == 0 || qstrcmp(QTest::currentDataTag(), "remote redirected") == 0 || qstrcmp(QTest::currentDataTag(), "remote svg") == 0 || qstrcmp(QTest::currentDataTag(), "remote svgz") == 0 || qstrcmp(QTest::currentDataTag(), "remote not found") == 0 ) { QSKIP("Remote tests cause occasional hangs in the CI system -- QTBUG-45655"); } #endif TestHTTPServer server; if (remote) { QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); server.addRedirect("oldcolors.png", server.urlString("/colors.png")); source = server.urlString(source); error.replace(QStringLiteral("{{ServerBaseUrl}}"), server.baseUrl().toString()); } if (!error.isEmpty()) QTest::ignoreMessage(QtWarningMsg, error.toUtf8()); QString componentStr = "import QtQuick 2.0\nImage { source: \"" + source + "\"; asynchronous: " + (async ? QLatin1String("true") : QLatin1String("false")) + "; cache: " + (cache ? QLatin1String("true") : QLatin1String("false")) + " }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); if (async) QVERIFY(obj->asynchronous()); else QVERIFY(!obj->asynchronous()); if (cache) QVERIFY(obj->cache()); else QVERIFY(!obj->cache()); if (remote || async) QTRY_COMPARE(obj->status(), QQuickImage::Loading); QCOMPARE(obj->source(), remote ? source : QUrl(source)); if (error.isEmpty()) { QTRY_COMPARE(obj->status(), QQuickImage::Ready); QCOMPARE(obj->width(), qreal(width)); QCOMPARE(obj->height(), qreal(height)); QCOMPARE(obj->fillMode(), QQuickImage::Stretch); QCOMPARE(obj->progress(), 1.0); } else { QTRY_COMPARE(obj->status(), QQuickImage::Error); } delete obj; } void tst_qquickimage::clearSource() { QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }"; QQmlContext *ctxt = engine.rootContext(); ctxt->setContextProperty("srcImage", testFileUrl("colors.png")); QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->status(), QQuickImage::Ready); QCOMPARE(obj->width(), 120.); QCOMPARE(obj->height(), 120.); QCOMPARE(obj->progress(), 1.0); ctxt->setContextProperty("srcImage", ""); QVERIFY(obj->source().isEmpty()); QCOMPARE(obj->status(), QQuickImage::Null); QCOMPARE(obj->width(), 0.); QCOMPARE(obj->height(), 0.); QCOMPARE(obj->progress(), 0.0); delete obj; } void tst_qquickimage::resized() { QString componentStr = "import QtQuick 2.0\nImage { source: \"" + testFile("colors.png") + "\"; width: 300; height: 300 }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), 300.); QCOMPARE(obj->height(), 300.); QCOMPARE(obj->fillMode(), QQuickImage::Stretch); delete obj; } void tst_qquickimage::preserveAspectRatio() { QScopedPointer window(new QQuickView(0)); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->setSource(testFileUrl("aspectratio.qml")); QQuickImage *image = qobject_cast(window->rootObject()); QVERIFY(image != 0); image->setWidth(80.0); QCOMPARE(image->width(), 80.); QCOMPARE(image->height(), 80.); window->setSource(testFileUrl("aspectratio.qml")); image = qobject_cast(window->rootObject()); image->setHeight(60.0); QVERIFY(image != 0); QCOMPARE(image->height(), 60.); QCOMPARE(image->width(), 60.); } void tst_qquickimage::smooth() { QString componentStr = "import QtQuick 2.0\nImage { source: \"" + testFile("colors.png") + "\"; smooth: true; width: 300; height: 300 }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), 300.); QCOMPARE(obj->height(), 300.); QCOMPARE(obj->smooth(), true); QCOMPARE(obj->fillMode(), QQuickImage::Stretch); delete obj; } void tst_qquickimage::mirror() { QMap screenshots; QList fillModes; fillModes << QQuickImage::Stretch << QQuickImage::PreserveAspectFit << QQuickImage::PreserveAspectCrop << QQuickImage::Tile << QQuickImage::TileVertically << QQuickImage::TileHorizontally << QQuickImage::Pad; qreal width = 300; qreal height = 250; foreach (QQuickImage::FillMode fillMode, fillModes) { #if defined(Q_OS_BLACKBERRY) QWindow dummy; // On BlackBerry first window is always full screen, dummy.showFullScreen(); // so make test window a second window. #endif QScopedPointer window(new QQuickView); window->setSource(testFileUrl("mirror.qml")); QQuickImage *obj = window->rootObject()->findChild("image"); QVERIFY(obj != 0); obj->setFillMode(fillMode); obj->setProperty("mirror", true); window->showNormal(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); QImage screenshot = window->grabWindow(); screenshots[fillMode] = screenshot; } foreach (QQuickImage::FillMode fillMode, fillModes) { QPixmap srcPixmap; QVERIFY(srcPixmap.load(testFile("pattern.png"))); QPixmap expected(width, height); expected.fill(); QPainter p_e(&expected); QTransform transform; transform.translate(width, 0).scale(-1, 1.0); p_e.setTransform(transform); QPoint offset(width / 2 - srcPixmap.width() / 2, height / 2 - srcPixmap.height() / 2); switch (fillMode) { case QQuickImage::Stretch: p_e.drawPixmap(QRect(0, 0, width, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height())); break; case QQuickImage::PreserveAspectFit: p_e.drawPixmap(QRect(25, 0, height, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height())); break; case QQuickImage::PreserveAspectCrop: { qreal ratio = width/srcPixmap.width(); // width is the longer side QRect rect(0, 0, srcPixmap.width()*ratio, srcPixmap.height()*ratio); rect.moveCenter(QRect(0, 0, width, height).center()); p_e.drawPixmap(rect, srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height())); break; } case QQuickImage::Tile: p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap, -offset); break; case QQuickImage::TileVertically: transform.scale(width / srcPixmap.width(), 1.0); p_e.setTransform(transform); p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap, QPoint(0, -offset.y())); break; case QQuickImage::TileHorizontally: transform.scale(1.0, height / srcPixmap.height()); p_e.setTransform(transform); p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap, QPoint(-offset.x(), 0)); break; case QQuickImage::Pad: p_e.drawPixmap(offset, srcPixmap); break; } QImage img = expected.toImage(); QCOMPARE(screenshots[fillMode], img); } } void tst_qquickimage::svg_data() { QTest::addColumn("src"); QTest::addColumn("format"); QTest::newRow("svg") << testFileUrl("heart.svg").toString() << QByteArray("svg"); QTest::newRow("svgz") << testFileUrl("heart.svgz").toString() << QByteArray("svgz"); } void tst_qquickimage::svg() { QFETCH(QString, src); QFETCH(QByteArray, format); if (!QImageReader::supportedImageFormats().contains(format)) QSKIP("svg support not available"); QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; sourceSize.width: 300; sourceSize.height: 300 }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), 300.0); QCOMPARE(obj->height(), 273.0); obj->setSourceSize(QSize(200,200)); QCOMPARE(obj->width(), 200.0); QCOMPARE(obj->height(), 182.0); delete obj; } void tst_qquickimage::geometry_data() { QTest::addColumn("fillMode"); QTest::addColumn("explicitWidth"); QTest::addColumn("explicitHeight"); QTest::addColumn("itemWidth"); QTest::addColumn("paintedWidth"); QTest::addColumn("boundingWidth"); QTest::addColumn("itemHeight"); QTest::addColumn("paintedHeight"); QTest::addColumn("boundingHeight"); // tested image has width 200, height 100 // bounding rect and item rect are equal with fillMode PreserveAspectFit, painted rect may be smaller if the aspect ratio doesn't match QTest::newRow("PreserveAspectFit") << "PreserveAspectFit" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0; QTest::newRow("PreserveAspectFit explicit width 300") << "PreserveAspectFit" << true << false << 300.0 << 200.0 << 300.0 << 100.0 << 100.0 << 100.0; QTest::newRow("PreserveAspectFit explicit height 400") << "PreserveAspectFit" << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 100.0 << 400.0; QTest::newRow("PreserveAspectFit explicit width 300, height 400") << "PreserveAspectFit" << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 150.0 << 400.0; // bounding rect and painted rect are equal with fillMode PreserveAspectCrop, item rect may be smaller if the aspect ratio doesn't match QTest::newRow("PreserveAspectCrop") << "PreserveAspectCrop" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0; QTest::newRow("PreserveAspectCrop explicit width 300") << "PreserveAspectCrop" << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 150.0 << 150.0; QTest::newRow("PreserveAspectCrop explicit height 400") << "PreserveAspectCrop" << false << true << 200.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0; QTest::newRow("PreserveAspectCrop explicit width 300, height 400") << "PreserveAspectCrop" << true << true << 300.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0; // bounding rect, painted rect and item rect are equal in stretching and tiling images QStringList fillModes; fillModes << "Stretch" << "Tile" << "TileVertically" << "TileHorizontally"; foreach (QString fillMode, fillModes) { QTest::newRow(fillMode.toLatin1()) << fillMode << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0; QTest::newRow(QString(fillMode + " explicit width 300").toLatin1()) << fillMode << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 100.0 << 100.0; QTest::newRow(QString(fillMode + " explicit height 400").toLatin1()) << fillMode << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 400.0 << 400.0; QTest::newRow(QString(fillMode + " explicit width 300, height 400").toLatin1()) << fillMode << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 400.0 << 400.0; } } void tst_qquickimage::geometry() { QFETCH(QString, fillMode); QFETCH(bool, explicitWidth); QFETCH(bool, explicitHeight); QFETCH(double, itemWidth); QFETCH(double, itemHeight); QFETCH(double, paintedWidth); QFETCH(double, paintedHeight); QFETCH(double, boundingWidth); QFETCH(double, boundingHeight); QString src = testFileUrl("rect.png").toString(); QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; fillMode: Image." + fillMode + "; "; if (explicitWidth) componentStr.append("width: 300; "); if (explicitHeight) componentStr.append("height: 400; "); componentStr.append("}"); QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), itemWidth); QCOMPARE(obj->paintedWidth(), paintedWidth); QCOMPARE(obj->boundingRect().width(), boundingWidth); QCOMPARE(obj->height(), itemHeight); QCOMPARE(obj->paintedHeight(), paintedHeight); QCOMPARE(obj->boundingRect().height(), boundingHeight); delete obj; } void tst_qquickimage::big() { // If the JPEG loader does not implement scaling efficiently, it would // have to build a 400 MB image. That would be a bug in the JPEG loader. QString src = testFileUrl("big.jpeg").toString(); QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 100; sourceSize.height: 256 }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), 100.0); QCOMPARE(obj->height(), 256.0); delete obj; } void tst_qquickimage::tiling_QTBUG_6716() { QFETCH(QString, source); QQuickView view(testFileUrl(source)); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); QQuickImage *tiling = findItem(view.rootObject(), "tiling"); QVERIFY(tiling != 0); QImage img = view.grabWindow(); for (int x = 0; x < tiling->width(); ++x) { for (int y = 0; y < tiling->height(); ++y) { QVERIFY(img.pixel(x, y) == qRgb(0, 255, 0)); } } } void tst_qquickimage::tiling_QTBUG_6716_data() { QTest::addColumn("source"); QTest::newRow("vertical_tiling") << "vtiling.qml"; QTest::newRow("horizontal_tiling") << "htiling.qml"; } void tst_qquickimage::noLoading() { qRegisterMetaType(); TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); server.addRedirect("oldcolors.png", server.urlString("/colors.png")); QString componentStr = "import QtQuick 2.0\nImage { source: srcImage; cache: true }"; QQmlContext *ctxt = engine.rootContext(); ctxt->setContextProperty("srcImage", testFileUrl("heart.png")); QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->status(), QQuickImage::Ready); QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(QUrl))); QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal))); QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status))); // Loading local file ctxt->setContextProperty("srcImage", testFileUrl("green.png")); QTRY_COMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->progress(), 1.0); QTRY_COMPARE(sourceSpy.count(), 1); QTRY_COMPARE(progressSpy.count(), 0); QTRY_COMPARE(statusSpy.count(), 1); // Loading remote file ctxt->setContextProperty("srcImage", server.url("/rect.png")); QTRY_COMPARE(obj->status(), QQuickImage::Loading); QTRY_COMPARE(obj->progress(), 0.0); QTRY_COMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->progress(), 1.0); QTRY_COMPARE(sourceSpy.count(), 2); QTRY_VERIFY(progressSpy.count() >= 2); QTRY_COMPARE(statusSpy.count(), 3); // Loading remote file again - should not go through 'Loading' state. progressSpy.clear(); ctxt->setContextProperty("srcImage", testFileUrl("green.png")); ctxt->setContextProperty("srcImage", server.url("/rect.png")); QTRY_COMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->progress(), 1.0); QTRY_COMPARE(sourceSpy.count(), 4); QTRY_COMPARE(progressSpy.count(), 0); QTRY_COMPARE(statusSpy.count(), 5); delete obj; } void tst_qquickimage::paintedWidthHeight() { { QString src = testFileUrl("heart.png").toString(); QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 200; height: 25; fillMode: Image.PreserveAspectFit }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), 200.0); QCOMPARE(obj->height(), 25.0); QCOMPARE(obj->paintedWidth(), 25.0); QCOMPARE(obj->paintedHeight(), 25.0); delete obj; } { QString src = testFileUrl("heart.png").toString(); QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 26; height: 175; fillMode: Image.PreserveAspectFit }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), 26.0); QCOMPARE(obj->height(), 175.0); QCOMPARE(obj->paintedWidth(), 26.0); QCOMPARE(obj->paintedHeight(), 26.0); delete obj; } } void tst_qquickimage::sourceSize_QTBUG_14303() { QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }"; QQmlContext *ctxt = engine.rootContext(); ctxt->setContextProperty("srcImage", testFileUrl("heart200.png")); QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QSignalSpy sourceSizeSpy(obj, SIGNAL(sourceSizeChanged())); QTRY_VERIFY(obj != 0); QTRY_COMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->sourceSize().width(), 200); QTRY_COMPARE(obj->sourceSize().height(), 200); QTRY_COMPARE(sourceSizeSpy.count(), 0); ctxt->setContextProperty("srcImage", testFileUrl("colors.png")); QTRY_COMPARE(obj->sourceSize().width(), 120); QTRY_COMPARE(obj->sourceSize().height(), 120); QTRY_COMPARE(sourceSizeSpy.count(), 1); ctxt->setContextProperty("srcImage", testFileUrl("heart200.png")); QTRY_COMPARE(obj->sourceSize().width(), 200); QTRY_COMPARE(obj->sourceSize().height(), 200); QTRY_COMPARE(sourceSizeSpy.count(), 2); delete obj; } void tst_qquickimage::sourceSize_QTBUG_16389() { QScopedPointer window(new QQuickView(0)); window->setSource(testFileUrl("qtbug_16389.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); QQuickImage *image = findItem(window->rootObject(), "iconImage"); QQuickItem *handle = findItem(window->rootObject(), "blueHandle"); QCOMPARE(image->sourceSize().width(), 200); QCOMPARE(image->sourceSize().height(), 200); QCOMPARE(image->paintedWidth(), 0.0); QCOMPARE(image->paintedHeight(), 0.0); handle->setY(20); QCOMPARE(image->sourceSize().width(), 200); QCOMPARE(image->sourceSize().height(), 200); QCOMPARE(image->paintedWidth(), 20.0); QCOMPARE(image->paintedHeight(), 20.0); } // QTBUG-15690 void tst_qquickimage::nullPixmapPaint() { QScopedPointer window(new QQuickView(0)); window->setSource(testFileUrl("nullpixmap.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQuickImage *image = qobject_cast(window->rootObject()); QTRY_VERIFY(image != 0); image->setSource(server.url("/no-such-file.png")); QQmlTestMessageHandler messageHandler; // used to print "QTransform::translate with NaN called" QPixmap pm = QPixmap::fromImage(window->grabWindow()); QVERIFY2(messageHandler.messages().size() == 0, qPrintable(messageHandler.messageString())); delete image; } void tst_qquickimage::imageCrash_QTBUG_22125() { TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); { QQuickView view; view.rootContext()->setContextProperty(QStringLiteral("serverBaseUrl"), server.baseUrl()); view.setSource(testFileUrl("qtbug_22125.qml")); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); qApp->processEvents(); // shouldn't crash when the view drops out of scope due to // QQuickPixmapData attempting to dereference a pointer to // the destroyed reader. } // shouldn't crash when deleting cancelled QQmlPixmapReplys. server.sendDelayedItem(); QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); QCoreApplication::processEvents(); } void tst_qquickimage::imageCrash_QTBUG_32513() { QQuickView view(testFileUrl("qtbug_32513.qml")); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); QTest::qWait(1000); // shouldn't crash when the image changes sources } void tst_qquickimage::sourceSize_data() { QTest::addColumn("sourceWidth"); QTest::addColumn("sourceHeight"); QTest::addColumn("implicitWidth"); QTest::addColumn("implicitHeight"); QTest::newRow("unscaled") << 0 << 0 << 300.0 << 300.0; QTest::newRow("scale width") << 100 << 0 << 100.0 << 100.0; QTest::newRow("scale height") << 0 << 150 << 150.0 << 150.0; QTest::newRow("larger sourceSize") << 400 << 400 << 300.0 << 300.0; } void tst_qquickimage::sourceSize() { QFETCH(int, sourceWidth); QFETCH(int, sourceHeight); QFETCH(qreal, implicitWidth); QFETCH(qreal, implicitHeight); QScopedPointer window(new QQuickView(0)); QQmlContext *ctxt = window->rootContext(); ctxt->setContextProperty("srcWidth", sourceWidth); ctxt->setContextProperty("srcHeight", sourceHeight); window->setSource(testFileUrl("sourceSize.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); QQuickImage *image = qobject_cast(window->rootObject()); QVERIFY(image); QCOMPARE(image->sourceSize().width(), sourceWidth); QCOMPARE(image->sourceSize().height(), sourceHeight); QCOMPARE(image->implicitWidth(), implicitWidth); QCOMPARE(image->implicitHeight(), implicitHeight); } void tst_qquickimage::sourceSizeChanges() { TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQuick 2.0\nImage { source: srcImage }", QUrl::fromLocalFile("")); QTRY_VERIFY(component.isReady()); QQmlContext *ctxt = engine.rootContext(); ctxt->setContextProperty("srcImage", ""); QQuickImage *img = qobject_cast(component.create()); QVERIFY(img != 0); QSignalSpy sourceSizeSpy(img, SIGNAL(sourceSizeChanged())); // Local ctxt->setContextProperty("srcImage", QUrl("")); QTRY_COMPARE(img->status(), QQuickImage::Null); QTRY_COMPARE(sourceSizeSpy.count(), 0); ctxt->setContextProperty("srcImage", testFileUrl("heart.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 1); ctxt->setContextProperty("srcImage", testFileUrl("heart.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 1); ctxt->setContextProperty("srcImage", testFileUrl("heart_copy.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 1); ctxt->setContextProperty("srcImage", testFileUrl("colors.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 2); ctxt->setContextProperty("srcImage", QUrl("")); QTRY_COMPARE(img->status(), QQuickImage::Null); QTRY_COMPARE(sourceSizeSpy.count(), 3); // Remote ctxt->setContextProperty("srcImage", server.url("/heart.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 4); ctxt->setContextProperty("srcImage", server.url("/heart.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 4); ctxt->setContextProperty("srcImage", server.url("/heart_copy.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 4); ctxt->setContextProperty("srcImage", server.url("/colors.png")); QTRY_COMPARE(img->status(), QQuickImage::Ready); QTRY_COMPARE(sourceSizeSpy.count(), 5); ctxt->setContextProperty("srcImage", QUrl("")); QTRY_COMPARE(img->status(), QQuickImage::Null); QTRY_COMPARE(sourceSizeSpy.count(), 6); delete img; } void tst_qquickimage::progressAndStatusChanges() { TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }"; QQmlContext *ctxt = engine.rootContext(); ctxt->setContextProperty("srcImage", testFileUrl("heart.png")); QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->progress(), 1.0); qRegisterMetaType(); QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(QUrl))); QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal))); QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status))); // Same image ctxt->setContextProperty("srcImage", testFileUrl("heart.png")); QTRY_COMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->progress(), 1.0); QTRY_COMPARE(sourceSpy.count(), 0); QTRY_COMPARE(progressSpy.count(), 0); QTRY_COMPARE(statusSpy.count(), 0); // Loading local file ctxt->setContextProperty("srcImage", testFileUrl("colors.png")); QTRY_COMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->progress(), 1.0); QTRY_COMPARE(sourceSpy.count(), 1); QTRY_COMPARE(progressSpy.count(), 0); QTRY_COMPARE(statusSpy.count(), 1); // Loading remote file ctxt->setContextProperty("srcImage", server.url("/heart.png")); QTRY_COMPARE(obj->status(), QQuickImage::Loading); QTRY_COMPARE(obj->progress(), 0.0); QTRY_COMPARE(obj->status(), QQuickImage::Ready); QTRY_COMPARE(obj->progress(), 1.0); QTRY_COMPARE(sourceSpy.count(), 2); QTRY_VERIFY(progressSpy.count() > 1); QTRY_COMPARE(statusSpy.count(), 3); ctxt->setContextProperty("srcImage", ""); QTRY_COMPARE(obj->status(), QQuickImage::Null); QTRY_COMPARE(obj->progress(), 0.0); QTRY_COMPARE(sourceSpy.count(), 3); QTRY_VERIFY(progressSpy.count() > 2); QTRY_COMPARE(statusSpy.count(), 4); delete obj; } class TestQImageProvider : public QQuickImageProvider { public: TestQImageProvider() : QQuickImageProvider(Image) {} QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize) { Q_UNUSED(requestedSize); if (id == QLatin1String("first-image.png")) { QTest::qWait(50); int width = 100; int height = 100; QImage image(width, height, QImage::Format_RGB32); image.fill(QColor("yellow").rgb()); if (size) *size = QSize(width, height); return image; } QTest::qWait(400); int width = 100; int height = 100; QImage image(width, height, QImage::Format_RGB32); image.fill(QColor("green").rgb()); if (size) *size = QSize(width, height); return image; } }; void tst_qquickimage::correctStatus() { QQmlEngine engine; engine.addImageProvider(QLatin1String("test"), new TestQImageProvider()); QQmlComponent component(&engine, testFileUrl("correctStatus.qml")); QObject *obj = component.create(); QVERIFY(obj); QTest::qWait(200); // at this point image1 should be attempting to load second-image.png, // and should be in the loading state. Without a clear prior to that load, // the status can mistakenly be in the ready state. QCOMPARE(obj->property("status").toInt(), int(QQuickImage::Loading)); QTest::qWait(400); delete obj; } void tst_qquickimage::highdpi() { QString componentStr = "import QtQuick 2.0\nImage { source: srcImage ; }"; QQmlComponent component(&engine); component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQmlContext *ctxt = engine.rootContext(); // Testing "@2x" high-dpi image loading: // The basic case is as follows. Suppose you have foo.png, // which is a 64x64 png that fits in a QML layout. Now, // on a high-dpi system that pixmap would not provide // enough pixels. To fix this the app developer provides // a 128x128 foo@2x.png, which Qt automatically loads. // The image continues to be referred to as "foo.png" in // the QML sources, and reports a size of 64x64. // // Load "heart-highdpi@2x.png", which is a 300x300 png. As a 2x scale image it // should render and report a geometry of 150x150. ctxt->setContextProperty("srcImage", testFileUrl("heart-highdpi@2x.png")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->width(), 150.0); QCOMPARE(obj->height(), 150.0); QCOMPARE(obj->paintedWidth(), 150.0); QCOMPARE(obj->paintedHeight(), 150.0); // Load a normal 1x image. ctxt->setContextProperty("srcImage", testFileUrl("heart.png")); QCOMPARE(obj->width(), 300.0); QCOMPARE(obj->height(), 300.0); QCOMPARE(obj->paintedWidth(), 300.0); QCOMPARE(obj->paintedHeight(), 300.0); delete obj; } void tst_qquickimage::hugeImages() { QQuickView view; view.setSource(testFileUrl("hugeImages.qml")); view.setGeometry(0, 0, 200, 200); view.create(); QImage contents = view.grabWindow(); QCOMPARE(contents.pixel(0, 0), qRgba(255, 0, 0, 255)); QCOMPARE(contents.pixel(99, 99), qRgba(255, 0, 0, 255)); QCOMPARE(contents.pixel(100, 0), qRgba(0, 0, 255, 255)); QCOMPARE(contents.pixel(199, 99), qRgba(0, 0, 255, 255)); } QTEST_MAIN(tst_qquickimage) #include "tst_qquickimage.moc"