diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | examples/svg/embedded/desktopservices/contenttab.cpp | 1 | ||||
-rw-r--r-- | examples/svg/embedded/fluidlauncher/pictureflow.cpp | 6 | ||||
-rw-r--r-- | examples/svg/embedded/weatherinfo/weatherinfo.cpp | 4 | ||||
-rw-r--r-- | examples/svg/network/bearercloud/cloud.cpp | 2 | ||||
-rw-r--r-- | examples/svg/svgviewer/mainwindow.cpp | 14 | ||||
-rw-r--r-- | examples/svg/svgviewer/mainwindow.h | 2 | ||||
-rw-r--r-- | examples/svg/svgviewer/svgview.cpp | 8 | ||||
-rw-r--r-- | examples/svg/svgviewer/svgview.h | 2 | ||||
-rw-r--r-- | src/svg/qsvggenerator.cpp | 2 | ||||
-rw-r--r-- | src/svg/qsvgtinydocument.cpp | 57 | ||||
-rw-r--r-- | src/svg/qsvgtinydocument_p.h | 9 | ||||
-rw-r--r-- | tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp | 10 | ||||
-rw-r--r-- | tests/libfuzzer/svg/qsvgrenderer/render/main.cpp | 42 | ||||
-rw-r--r-- | tests/libfuzzer/svg/qsvgrenderer/render/render.pro | 3 | ||||
-rw-r--r-- | tests/manual/rendertestsuite/.gitignore | 2 | ||||
-rw-r--r-- | tests/manual/rendertestsuite/README | 32 | ||||
-rw-r--r-- | tests/manual/rendertestsuite/main.cpp | 139 | ||||
-rw-r--r-- | tests/manual/rendertestsuite/rendertestsuite.pro | 3 |
19 files changed, 290 insertions, 50 deletions
diff --git a/.qmake.conf b/.qmake.conf index b5e35ea..c6f1702 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -3,4 +3,4 @@ load(qt_build_config) CONFIG += warning_clean DEFINES += QT_NO_FOREACH -MODULE_VERSION = 5.13.1 +MODULE_VERSION = 5.14.0 diff --git a/examples/svg/embedded/desktopservices/contenttab.cpp b/examples/svg/embedded/desktopservices/contenttab.cpp index 8d467df..2dac351 100644 --- a/examples/svg/embedded/desktopservices/contenttab.cpp +++ b/examples/svg/embedded/desktopservices/contenttab.cpp @@ -124,6 +124,7 @@ void ContentTab::keyPressEvent(QKeyEvent *event) switch (event->key()) { case Qt::Key_Select: openItem(currentItem()); + Q_FALLTHROUGH(); default: QListWidget::keyPressEvent(event); break; diff --git a/examples/svg/embedded/fluidlauncher/pictureflow.cpp b/examples/svg/embedded/fluidlauncher/pictureflow.cpp index 191ff2e..b0657a2 100644 --- a/examples/svg/embedded/fluidlauncher/pictureflow.cpp +++ b/examples/svg/embedded/fluidlauncher/pictureflow.cpp @@ -85,7 +85,7 @@ #include <QTimer> #include <QVector> #include <QWidget> -#include <QTime> +#include <QElapsedTimer> #include <QDebug> @@ -376,7 +376,7 @@ public: int singlePressThreshold; QPoint firstPress; QPoint previousPos; - QTime previousPosTimestamp; + QElapsedTimer previousPosTimestamp; int pixelDistanceMoved; int pixelsToMovePerSlide; @@ -1397,7 +1397,7 @@ void PictureFlow::timerEvent(QTimerEvent* event) { if(event->timerId() == d->animateTimer.timerId()) { -// QTime now = QTime::currentTime(); +// QElapsedTimer now; now.start(); d->updateAnimation(); // d->animateTimer.start(qMax(0, 30-now.elapsed() ), this); } diff --git a/examples/svg/embedded/weatherinfo/weatherinfo.cpp b/examples/svg/embedded/weatherinfo/weatherinfo.cpp index b64d8ca..21aa986 100644 --- a/examples/svg/embedded/weatherinfo/weatherinfo.cpp +++ b/examples/svg/embedded/weatherinfo/weatherinfo.cpp @@ -147,17 +147,13 @@ private slots: void animate(int frame) { qreal progress = static_cast<qreal>(frame) / 100; -#if QT_VERSION >= 0x040500 m_iconItem->setOpacity(progress); -#endif qreal hw = width() / 2.0; m_statusItem->setPos(-hw + hw * progress, 0); for (int i = 0; i < m_forecastItems.count(); ++i) { qreal ofs = i * 0.5 / m_forecastItems.count(); qreal alpha = qBound(qreal(0), 2 * (progress - ofs), qreal(1)); -#if QT_VERSION >= 0x040500 m_conditionItems[i]->setOpacity(alpha); -#endif QPointF pos = m_forecastItems[i]->pos(); if (width() > height()) { qreal fx = width() - width() * 0.4 * alpha; diff --git a/examples/svg/network/bearercloud/cloud.cpp b/examples/svg/network/bearercloud/cloud.cpp index 4e0313a..1117c94 100644 --- a/examples/svg/network/bearercloud/cloud.cpp +++ b/examples/svg/network/bearercloud/cloud.cpp @@ -76,9 +76,7 @@ Cloud::Cloud(const QNetworkConfiguration &config, QGraphicsItem *parent) this, SLOT(stateChanged(QNetworkSession::State))); setFlag(ItemIsMovable); -#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) setFlag(ItemSendsGeometryChanges); -#endif setZValue(1); icon = new QGraphicsSvgItem(this); diff --git a/examples/svg/svgviewer/mainwindow.cpp b/examples/svg/svgviewer/mainwindow.cpp index 7dd240a..d095416 100644 --- a/examples/svg/svgviewer/mainwindow.cpp +++ b/examples/svg/svgviewer/mainwindow.cpp @@ -118,14 +118,10 @@ MainWindow::MainWindow() m_imageAction->setData(int(SvgView::Image)); rendererMenu->addSeparator(); - m_highQualityAntialiasingAction = rendererMenu->addAction(tr("&High Quality Antialiasing")); - m_highQualityAntialiasingAction->setEnabled(false); - m_highQualityAntialiasingAction->setCheckable(true); - m_highQualityAntialiasingAction->setChecked(false); - connect(m_highQualityAntialiasingAction, &QAction::toggled, m_view, &SvgView::setHighQualityAntialiasing); -#ifdef QT_NO_OPENGL - m_highQualityAntialiasingAction->setVisible(false); -#endif + m_antialiasingAction = rendererMenu->addAction(tr("&Antialiasing")); + m_antialiasingAction->setCheckable(true); + m_antialiasingAction->setChecked(false); + connect(m_antialiasingAction, &QAction::toggled, m_view, &SvgView::setAntialiasing); QActionGroup *rendererGroup = new QActionGroup(this); rendererGroup->addAction(m_nativeAction); @@ -191,8 +187,6 @@ bool MainWindow::loadFile(const QString &fileName) void MainWindow::setRenderer(int renderMode) { - - m_highQualityAntialiasingAction->setEnabled(renderMode == SvgView::OpenGL); m_view->setRenderer(static_cast<SvgView::RendererType>(renderMode)); } diff --git a/examples/svg/svgviewer/mainwindow.h b/examples/svg/svgviewer/mainwindow.h index 6c2af7e..a102eaa 100644 --- a/examples/svg/svgviewer/mainwindow.h +++ b/examples/svg/svgviewer/mainwindow.h @@ -85,7 +85,7 @@ private: QAction *m_nativeAction; QAction *m_glAction; QAction *m_imageAction; - QAction *m_highQualityAntialiasingAction; + QAction *m_antialiasingAction; QAction *m_backgroundAction; QAction *m_outlineAction; diff --git a/examples/svg/svgviewer/svgview.cpp b/examples/svg/svgviewer/svgview.cpp index ecc8409..50b622b 100644 --- a/examples/svg/svgviewer/svgview.cpp +++ b/examples/svg/svgviewer/svgview.cpp @@ -153,13 +153,9 @@ void SvgView::setRenderer(RendererType type) } } -void SvgView::setHighQualityAntialiasing(bool highQualityAntialiasing) +void SvgView::setAntialiasing(bool antialiasing) { -#ifndef QT_NO_OPENGL - setRenderHint(QPainter::HighQualityAntialiasing, highQualityAntialiasing); -#else - Q_UNUSED(highQualityAntialiasing); -#endif + setRenderHint(QPainter::Antialiasing, antialiasing); } void SvgView::setViewBackground(bool enable) diff --git a/examples/svg/svgviewer/svgview.h b/examples/svg/svgviewer/svgview.h index 1b6b33e..7d5f7d1 100644 --- a/examples/svg/svgviewer/svgview.h +++ b/examples/svg/svgviewer/svgview.h @@ -79,7 +79,7 @@ public: qreal zoomFactor() const; public slots: - void setHighQualityAntialiasing(bool highQualityAntialiasing); + void setAntialiasing(bool antialiasing); void setViewBackground(bool enable); void setViewOutline(bool enable); void zoomIn(); diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp index 07f8d74..b671c44 100644 --- a/src/svg/qsvggenerator.cpp +++ b/src/svg/qsvggenerator.cpp @@ -1009,7 +1009,7 @@ void QSvgPaintEngine::updateState(const QPaintEngineState &state) } if (flags & QPaintEngine::DirtyTransform) { - d->matrix = state.matrix(); + d->matrix = state.transform().toAffine(); *d->stream << "transform=\"matrix(" << d->matrix.m11() << ',' << d->matrix.m12() << ',' << d->matrix.m21() << ',' << d->matrix.m22() << ',' diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp index da464cc..3143ad2 100644 --- a/src/svg/qsvgtinydocument.cpp +++ b/src/svg/qsvgtinydocument.cpp @@ -60,6 +60,7 @@ QSvgTinyDocument::QSvgTinyDocument() : QSvgStructureNode(0) , m_widthPercent(false) , m_heightPercent(false) + , m_time(0) , m_animated(false) , m_animationDuration(0) , m_fps(30) @@ -230,9 +231,8 @@ QSvgTinyDocument * QSvgTinyDocument::load(QXmlStreamReader *contents) void QSvgTinyDocument::draw(QPainter *p, const QRectF &bounds) { - if (m_time.isNull()) { - m_time.start(); - } + if (m_time == 0) + m_time = QDateTime::currentMSecsSinceEpoch(); if (displayMode() == QSvgNode::NoneMode) return; @@ -269,9 +269,8 @@ void QSvgTinyDocument::draw(QPainter *p, const QString &id, qCDebug(lcSvgHandler, "Couldn't find node %s. Skipping rendering.", qPrintable(id)); return; } - if (m_time.isNull()) { - m_time.start(); - } + if (m_time == 0) + m_time = QDateTime::currentMSecsSinceEpoch(); if (node->displayMode() == QSvgNode::NoneMode) return; @@ -339,6 +338,7 @@ void QSvgTinyDocument::setHeight(int len, bool percent) void QSvgTinyDocument::setViewBox(const QRectF &rect) { m_viewBox = rect; + m_implicitViewBox = false; } void QSvgTinyDocument::addSvgFont(QSvgFont *font) @@ -376,7 +376,7 @@ QSvgFillStyleProperty *QSvgTinyDocument::namedStyle(const QString &id) const void QSvgTinyDocument::restartAnimation() { - m_time.restart(); + m_time = QDateTime::currentMSecsSinceEpoch(); } bool QSvgTinyDocument::animated() const @@ -420,14 +420,35 @@ void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, source = viewBox(); if (source != target && !source.isNull()) { - QTransform transform; - transform.scale(target.width() / source.width(), - target.height() / source.height()); - QRectF c2 = transform.mapRect(source); - p->translate(target.x() - c2.x(), - target.y() - c2.y()); - p->scale(target.width() / source.width(), - target.height() / source.height()); + if (m_implicitViewBox) { + QTransform transform; + transform.scale(target.width() / source.width(), + target.height() / source.height()); + QRectF c2 = transform.mapRect(source); + p->translate(target.x() - c2.x(), + target.y() - c2.y()); + p->scale(target.width() / source.width(), + target.height() / source.height()); + } else { + // Code path used when a view box is specified and we're not rendering a specific element by id + // but the entire document. This attempts to emulate the default values of the <preserveAspectRatio> + // tag that's implicitly defined when <viewbox> is used. + + // Apply the view box translation if specified. + p->translate(target.x() - source.x(), + target.y() - source.y()); + + // Scale the view box into the view port (target) by preserve the aspect ratio. + QSizeF viewBoxSize = source.size(); + viewBoxSize.scale(target.width(), target.height(), Qt::KeepAspectRatio); + + // Center the view box in the view port + p->translate((target.width() - viewBoxSize.width()) / 2, + (target.height() - viewBoxSize.height()) / 2); + + p->scale(viewBoxSize.width() / source.width(), + viewBoxSize.height() / source.height()); + } } } @@ -469,7 +490,7 @@ QMatrix QSvgTinyDocument::matrixForElement(const QString &id) const int QSvgTinyDocument::currentFrame() const { - double runningPercentage = qMin(m_time.elapsed()/double(m_animationDuration), 1.); + double runningPercentage = qMin(currentElapsed() / double(m_animationDuration), 1.); int totalFrames = m_fps * m_animationDuration; @@ -482,8 +503,8 @@ void QSvgTinyDocument::setCurrentFrame(int frame) double framePercentage = frame/double(totalFrames); double timeForFrame = m_animationDuration * framePercentage; //in S timeForFrame *= 1000; //in ms - int timeToAdd = int(timeForFrame - m_time.elapsed()); - m_time = m_time.addMSecs(timeToAdd); + int timeToAdd = int(timeForFrame - currentElapsed()); + m_time += timeToAdd; } void QSvgTinyDocument::setFramesPerSecond(int num) diff --git a/src/svg/qsvgtinydocument_p.h b/src/svg/qsvgtinydocument_p.h index aa51751..404587d 100644 --- a/src/svg/qsvgtinydocument_p.h +++ b/src/svg/qsvgtinydocument_p.h @@ -125,13 +125,14 @@ private: bool m_widthPercent; bool m_heightPercent; + mutable bool m_implicitViewBox = true; mutable QRectF m_viewBox; QHash<QString, QSvgRefCounter<QSvgFont> > m_fonts; QHash<QString, QSvgNode *> m_namedNodes; QHash<QString, QSvgRefCounter<QSvgFillStyleProperty> > m_namedStyles; - QTime m_time; + qint64 m_time; bool m_animated; int m_animationDuration; int m_fps; @@ -173,8 +174,10 @@ inline bool QSvgTinyDocument::heightPercent() const inline QRectF QSvgTinyDocument::viewBox() const { - if (m_viewBox.isNull()) + if (m_viewBox.isNull()) { m_viewBox = transformedBounds(); + m_implicitViewBox = true; + } return m_viewBox; } @@ -186,7 +189,7 @@ inline bool QSvgTinyDocument::preserveAspectRatio() const inline int QSvgTinyDocument::currentElapsed() const { - return m_time.elapsed(); + return QDateTime::currentMSecsSinceEpoch() - m_time; } inline int QSvgTinyDocument::animationDuration() const diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index e0646f2..cf19213 100644 --- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -268,6 +268,16 @@ void tst_QSvgRenderer::testMapViewBoxToTarget() QCOMPARE(picture.boundingRect(), QRect(125, 125, 250, 250)); } + { // Viewport and viewBox specified -> scale 500x500 square to 1000x750 while preserving aspect ratio gives 750x750 + // however the box is centered at 375, 250 + data = "<svg width=\"1000\" height=\"750\" viewBox=\"-250 -250 500 500\"><g><rect x=\"0\" y=\"0\" width=\"500\" height=\"500\" /></g></svg>"; + QPicture picture; + QPainter painter(&picture); + QSvgRenderer rend(data); + rend.render(&painter); + painter.end(); + QCOMPARE(picture.boundingRect(), QRect(375, 250, 750, 750)); + } } void tst_QSvgRenderer::testRenderElement() diff --git a/tests/libfuzzer/svg/qsvgrenderer/render/main.cpp b/tests/libfuzzer/svg/qsvgrenderer/render/main.cpp new file mode 100644 index 0000000..0197115 --- /dev/null +++ b/tests/libfuzzer/svg/qsvgrenderer/render/main.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QImage> +#include <QPainter> +#include <QSvgRenderer> + +extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { + static int c = 0; + static QApplication a(c, nullptr); + static QImage image(377, 233, QImage::Format_RGB32); + static QPainter painter(&image); + QSvgRenderer renderer(QByteArray(Data, Size)); + renderer.render(&painter); + return 0; +} diff --git a/tests/libfuzzer/svg/qsvgrenderer/render/render.pro b/tests/libfuzzer/svg/qsvgrenderer/render/render.pro new file mode 100644 index 0000000..be94755 --- /dev/null +++ b/tests/libfuzzer/svg/qsvgrenderer/render/render.pro @@ -0,0 +1,3 @@ +QT += svg +SOURCES += main.cpp +LIBS += -fsanitize=fuzzer diff --git a/tests/manual/rendertestsuite/.gitignore b/tests/manual/rendertestsuite/.gitignore new file mode 100644 index 0000000..54f7094 --- /dev/null +++ b/tests/manual/rendertestsuite/.gitignore @@ -0,0 +1,2 @@ +baseline +difference diff --git a/tests/manual/rendertestsuite/README b/tests/manual/rendertestsuite/README new file mode 100644 index 0000000..0609d4f --- /dev/null +++ b/tests/manual/rendertestsuite/README @@ -0,0 +1,32 @@ +This little helper program can be used together with the SVG Tiny 1.2 test +suite to determine the visual impact of changes to the renderer on the basis of +the test suite. + +Suppose you have the test suite installed in ~/tests, then you can create a +base line of renderings of all the SVG images by running + + mkdir baseline + ./rendertestsuite create-baseline ~/tests + +and the output will be stored in the 'baseline/' sub-directory. + +After making changes to the renderer, you can create a new set of images by running + + mkdir difference + ./rendertestsuite diff ~/tests + +and side-by-side images of the old and the new images in case they differ will +be stored in the sub-directory 'difference/'. + +An easy way to inspect the output is by using KDE's gwenview: + + gwenview difference/ + + +Note that due to rounding errors there will always be subtle differences, even +when not doing any changes to the renderer. + + +You can find the test suite at + + https://www.w3.org/Graphics/SVG/WG/wiki/Test_Suite_Overview#SVG_1.2_Tiny_Test_Suite:_12_September_2008 diff --git a/tests/manual/rendertestsuite/main.cpp b/tests/manual/rendertestsuite/main.cpp new file mode 100644 index 0000000..dfdf88c --- /dev/null +++ b/tests/manual/rendertestsuite/main.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <QtSvg> +#include <QtGui> +#include <stdlib.h> + +static QImage render(const QString &filePath) +{ + fprintf(stdout, "Rendering %s\n", qPrintable(filePath)); + QSvgRenderer renderer(filePath); + if (!renderer.isValid()) { + fprintf(stderr, "Could not load SVG file %s\n", qPrintable(filePath)); + return QImage(); + } + QImage image(480, 360, QImage::Format_ARGB32); + image.fill(Qt::transparent); + { + QPainter p(&image); + renderer.render(&p); + } + return image; +} + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + QCommandLineParser parser; + parser.addHelpOption(); + parser.addPositionalArgument(QLatin1String("command"), QLatin1String("[create-baseline,diff]")); + parser.addPositionalArgument(QLatin1String("path-to-svg-testsuite"), QLatin1String("Path to the svg/ sub-directory of the test suite")); + + parser.process(app); + + const auto args = parser.positionalArguments(); + + if (args.count() != 2) { + parser.showHelp(EXIT_FAILURE); + return EXIT_FAILURE; // never reached + } + + const QString commandAsString = args.at(0); + const QString sourcePath = args.at(1); + + QDirIterator sourceFileIterator(sourcePath, QStringList(QLatin1String("*.svg")), QDir::Files); + const QString baselinePath = "baseline"; + + const auto referenceFilePath = [baselinePath](const QFileInfo &testCaseFileInfo) -> QString { + return baselinePath + QLatin1Char('/') + testCaseFileInfo.baseName() + QLatin1String(".png"); + }; + + if (commandAsString == "create-baseline") { + while (sourceFileIterator.hasNext()) { + sourceFileIterator.next(); + + QImage image = render(sourceFileIterator.filePath()); + if (image.isNull()) + return EXIT_FAILURE; + QString outputFileName = referenceFilePath(sourceFileIterator.fileInfo()); + if (!image.save(outputFileName)) { + fprintf(stderr, "Could not save PNG file %s\n", qPrintable(outputFileName)); + return EXIT_FAILURE; + } + } + } else if (commandAsString == "diff") { + while (sourceFileIterator.hasNext()) { + sourceFileIterator.next(); + + QImage actual = render(sourceFileIterator.filePath()); + const QString referencePath = referenceFilePath(sourceFileIterator.fileInfo()); + QImage reference; + if (!reference.load(referencePath)) { + fprintf(stderr, "Could not load reference file %s\n", qPrintable(referencePath)); + return EXIT_FAILURE; + } + + if (actual == reference) + continue; + + QImage sideBySideImage(actual.width() * 2, actual.height(), QImage::Format_ARGB32); + sideBySideImage.fill(Qt::transparent); + { + QPainter p(&sideBySideImage); + p.drawImage(0, 0, actual); + p.drawImage(actual.width(), 0, reference); + } + + const QString sideBySideFileName = "difference/" + sourceFileIterator.fileInfo().baseName() + QLatin1String(".png"); + if (!sideBySideImage.save(sideBySideFileName)) { + fprintf(stderr, "Could not save side-by-side image at %s\n", qPrintable(sideBySideFileName)); + return EXIT_FAILURE; + } + } + } else { + fprintf(stderr, "Unknown command %s\n", qPrintable(commandAsString)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + diff --git a/tests/manual/rendertestsuite/rendertestsuite.pro b/tests/manual/rendertestsuite/rendertestsuite.pro new file mode 100644 index 0000000..855d3fc --- /dev/null +++ b/tests/manual/rendertestsuite/rendertestsuite.pro @@ -0,0 +1,3 @@ +TEMPLATE = app +QT = core gui svg +SOURCES = main.cpp |