diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 10:18:55 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 10:18:55 +0100 |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tests/auto/uiloader/uiloader/uiloader.cpp |
Long live Qt 4.5!
Diffstat (limited to 'tests/auto/uiloader/uiloader/uiloader.cpp')
-rw-r--r-- | tests/auto/uiloader/uiloader/uiloader.cpp | 814 |
1 files changed, 814 insertions, 0 deletions
diff --git a/tests/auto/uiloader/uiloader/uiloader.cpp b/tests/auto/uiloader/uiloader/uiloader.cpp new file mode 100644 index 0000000000..2bc59b4932 --- /dev/null +++ b/tests/auto/uiloader/uiloader/uiloader.cpp @@ -0,0 +1,814 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "uiloader.h" + +#include <QtCore/QDir> +#include <QtCore/QProcess> +#include <QtCore/QSettings> +#include <QtCore/QCoreApplication> + +#include <QtTest/QSignalSpy> +#include <QTest> + +#include <QString> +#include <QHash> +#include <QFile> +#include <QFtp> +#include <QObject> +#include <QHostInfo> +#include <QWidget> +#include <QImage> + +#include <QLibraryInfo> + + +/* + * Our own QVERIFY since the one from QTest can't be used in non-void functions. + * Just pass the desired return value as third argument. + */ + +#define QVERIFY3(statement, description, returnValue) \ +do {\ + if (statement) {\ + if (!QTest::qVerify(true, #statement, (description), __FILE__, __LINE__))\ + return returnValue;\ + } else {\ + if (!QTest::qVerify(false, #statement, (description), __FILE__, __LINE__))\ + return returnValue;\ + }\ +} while (0) + + + +uiLoader::uiLoader(const QString &_pathToProgram) + : pathToProgram(_pathToProgram) +{ + // initTests(); +} + + + + +/* + * Load the configuration file for your machine. + * Return true if everything was loaded, else false. + * + * If the hostname is 'kayak', the config file should be 'kayak.ini': + * + * [General] + * ftpBaseDir=/arthurtest + * ftpHost=wartburg + * ftpPass=anonymouspass + * ftpUser=anonymous + * output=testresults + * + * [engines] + * 1\engine=uic + * size=1 + */ + +bool uiLoader::loadConfig(const QString &filePath, QString *errorMessage) +{ + qDebug() << " ========== Loading config file " << filePath; + configPath = filePath; + + // If there is no config file, dont proceed; + QSettings settings( filePath, QSettings::IniFormat, this ); + + // all keys available? + QStringList keyList; + keyList << QLatin1String("output") << QLatin1String("ftpUser") << QLatin1String("ftpPass") << QLatin1String("ftpHost") << QLatin1String("ftpBaseDir"); + for (int i = 0; i < keyList.size(); ++i) { + const QString currentKey = keyList.at(i); + if (!settings.contains(currentKey)) { + *errorMessage = QString::fromLatin1("Config file '%1' does not contain the required key '%2'.").arg(filePath, currentKey); + return false; + } + + qDebug() << "\t\t(I)" << currentKey << "\t" << settings.value(currentKey).toString(); + } + + const int size = settings.beginReadArray(QLatin1String("engines")); + if (!size) { + *errorMessage = QString::fromLatin1("Config file '%1' does not contain the necessary section engines.").arg(filePath); + return false; + } + + // get the values + for ( int i = 0; i < size; ++i ) { + settings.setArrayIndex(i); + qDebug() << "\t\t(I)" << "engine" << "\t" << settings.value( "engine" ).toString(); + enginesToTest.insert(settings.value(QLatin1String("engine")).toString(), QLatin1String("Info here please :p")); + } + settings.endArray(); + + output = settings.value(QLatin1String("output")).toString(); + output += QDir::separator() + QLibraryInfo::buildKey() + QDir::separator() + QString( qVersion() ); + ftpUser = settings.value( QLatin1String("ftpUser") ).toString(); + ftpPass = settings.value( QLatin1String("ftpPass") ).toString(); + ftpHost = settings.value( QLatin1String("ftpHost") ).toString(); + ftpBaseDir = settings.value( QLatin1String("ftpBaseDir") ).toString() + QDir::separator() + QHostInfo::localHostName().split( QLatin1Char('.')).first(); + threshold = settings.value( QLatin1String("threshold") ).toString(); + + qDebug() << "\t(I) Values adapted:"; + qDebug() << "\t\t(I)" << "ftpBaseDir" << "\t" << ftpBaseDir; + qDebug() << "\t\t(I)" << "output" << "\t" << output; + + return true; +} + +/* + * Upload testresults to the server in order to create the new baseline. + */ + +void uiLoader::createBaseline() +{ + // can't use ftpUploadFile() here + qDebug() << " ========== Uploading baseline of only the latest test values "; + + QFtp ftp; + ftp.connectToHost( ftpHost ); + ftp.login( ftpUser, ftpPass ); + ftp.cd( ftpBaseDir ); + + QDir dir( output ); + + // Upload all the latest test results to the FTP server's baseline directory. + QHashIterator<QString, QString> i(enginesToTest); + while ( i.hasNext() ) { + i.next(); + + dir.cd( i.key() ); + ftp.cd( i.key() + ".baseline" ); + + dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); + dir.setNameFilters( QStringList() << "*.png" ); + QFileInfoList list = dir.entryInfoList(); + + dir.cd( ".." ); + + for (int n = 0; n < list.size(); n++) { + QFileInfo fileInfo = list.at( n ); + QFile file( QString( output ) + "/" + i.key() + "/" + fileInfo.fileName() ); + + errorMsg = "could not open file " + fileInfo.fileName(); + QVERIFY2( file.open(QIODevice::ReadOnly), qPrintable(errorMsg)); + + QByteArray fileData = file.readAll(); + file.close(); + + ftp.put( fileData, fileInfo.fileName(), QFtp::Binary ); + qDebug() << "\t(I) Uploading:" << fileInfo.fileName() << "with file size" << fileData.size(); + } + + ftp.cd( ".." ); + } + + ftp.close(); + + while ( ftp.hasPendingCommands() ) + QCoreApplication::instance()->processEvents(); +} + + + +/* + * Download baseline from server in order to compare results. + */ + +void uiLoader::downloadBaseline() +{ + qDebug() << " ========== Downloading baseline..."; + + QHashIterator<QString, QString> i(enginesToTest); + while ( i.hasNext() ) { + i.next(); + QString engineName = i.key(); + + QString dirWithFiles = ftpBaseDir + '/' + engineName + ".baseline"; + QString ftpDir = ftpBaseDir + '/' + engineName + ".baseline"; + QString saveToDir = QDir::currentPath() + '/' + output + '/' + engineName + ".baseline"; + + ftpList(dirWithFiles); + + QList<QString> filesToDownload(lsDirList); + ftpGetFiles(filesToDownload, ftpDir, saveToDir); + } +} + + + +/* + * Enter the dir pathDir local and remove all files (not recursive!) + */ + +void uiLoader::clearDirectory(const QString& pathDir) +{ + qDebug() << "\t(I) Clearing directory local: " << pathDir; + + QDir dir(pathDir); + dir.setFilter(QDir::Files); + QStringList list = dir.entryList(); + + for (int n = 0; n < list.size(); n++) { + QString filePath = pathDir + "/" + list.at(n); + QFile file(filePath); + + errorMsg = "could not remove file " + filePath; + QVERIFY2( file.remove(), qPrintable(errorMsg)); + } + +} + + + +/* + * Setup the local environment. + */ + +void uiLoader::setupLocal() +{ + qDebug( " ========== Setting up local environment" ); + + QDir dir; + + errorMsg = "could not create path " + output; + QVERIFY2( dir.mkpath(output), qPrintable(errorMsg) ); + + QHashIterator<QString, QString> j(enginesToTest); + while ( j.hasNext() ) { + j.next(); + + QString engineName = j.key(); + QString engineDir = output + '/' + engineName; + + // create <engine> or clean it + QString tmpPath = output + '/' + engineName; + if ( dir.exists(tmpPath) ) { + clearDirectory(tmpPath); + } else { + dir.mkdir(tmpPath); + } + + // create *.baseline or clean it + tmpPath = output + '/' + engineName + ".baseline"; + if ( dir.exists(tmpPath) ) { + clearDirectory(tmpPath); + } else { + dir.mkdir(tmpPath); + } + + // create *.diff or clean it + tmpPath = output + '/' + engineName + ".diff"; + if ( dir.exists(tmpPath) ) { + clearDirectory(tmpPath); + } else { + dir.mkdir(tmpPath); + } + + // create *.failed or clean it + tmpPath = output + '/' + engineName + ".failed"; + if ( dir.exists(tmpPath) ) { + clearDirectory(tmpPath); + } else { + dir.mkdir(tmpPath); + } + } + + qDebug() << "\t(I) Created on local machine:" << output; +} + + + +/* + * Setup the remote environment. + */ + +void uiLoader::setupFTP() +{ + qDebug( " ========== Setting up FTP environment" ); + + // create dirs on ftp server + ftpMkDir( ftpBaseDir ); + ftpBaseDir += "/" + QLibraryInfo::buildKey(); + ftpMkDir( ftpBaseDir ); + ftpBaseDir += "/" + QString( qVersion() ); + ftpMkDir( ftpBaseDir ); + + QString dir = ""; + ftpList(ftpBaseDir + '/' + dir); + QList<QString> dirListing(lsDirList); + + // create *.failed, *.diff if necessary, else remove the files in it + // if *.baseline does not exist, memorize it + QHashIterator<QString, QString> j(enginesToTest); + while ( j.hasNext() ) { + j.next(); + + QString curDir = QString( j.key() ) + ".failed"; + if ( dirListing.contains( curDir ) ) { + ftpClearDirectory(ftpBaseDir + "/" + curDir + "/"); + } else { + ftpMkDir(ftpBaseDir + "/" + curDir + "/"); + } + + curDir = QString( j.key() ) + ".diff"; + if ( dirListing.contains( curDir ) ) { + ftpClearDirectory(ftpBaseDir + "/" + curDir + "/"); + } else { + ftpMkDir(ftpBaseDir + "/" + curDir + "/"); + } + + curDir = QString( j.key() ) + ".baseline"; + lsNeedBaseline.clear(); + if ( !dirListing.contains( curDir ) ) { + ftpMkDir(ftpBaseDir + "/" + curDir + "/"); + lsNeedBaseline << j.key(); + } else { + qDebug() << "\t(I)" << curDir << "exists on server."; + } + } +} + + + +/* + * Download files listed in fileLisiting from dir pathRemoteDir on sever and save + * them in pathSaveDir. + */ + +void uiLoader::ftpGetFiles(QList<QString>& fileListing, const QString& pathRemoteDir, const QString& pathSaveDir) +{ + QFtp ftp; + ftp.connectToHost( ftpHost ); + ftp.login( ftpUser, ftpPass ); + + if ( !fileListing.empty() ) { + for ( int i = 0; i < fileListing.size(); ++i ) { + QFile file( pathSaveDir + "/" + fileListing.at(i) ); + + errorMsg = "could not open file for writing: " + file.fileName(); + QVERIFY2( file.open(QIODevice::WriteOnly), qPrintable(errorMsg) ); + + QString ftpFileName = pathRemoteDir + '/' + fileListing.at(i); + ftp.get( ftpFileName, &file ); + //qDebug() << "\t(I) Got" << file.fileName(); + ftp.list(); //Only there to fill up a slot in the pendingCommands queue. + + while ( ftp.hasPendingCommands() ) + QCoreApplication::instance()->processEvents(); + + file.close(); + } + } + + ftp.close(); + + while ( ftp.hasPendingCommands() ) + QCoreApplication::instance()->processEvents(); + + fileListing.clear(); +} + + + +/* + * Upload the file filePath to the server and save it there at filePathRemote. + * + * HINT: It seems you can't use this function in a loop, to many connections + * are established?! + */ + +bool uiLoader::ftpUploadFile(const QString& filePathRemote, const QString& filePath) +{ + QFile file(filePath); + + errorMsg = "could not open file: " + filePath; + QVERIFY3( file.open(QIODevice::ReadOnly), qPrintable(errorMsg), false ); + + QByteArray contents = file.readAll(); + file.close(); + + qDebug() << "\t(I) Uploading file to" << filePathRemote; + + QFtp ftp; + ftp.connectToHost( ftpHost ); + ftp.login( ftpUser, ftpPass ); + + ftp.put( contents, filePathRemote, QFtp::Binary ); + + ftp.close(); + + while ( ftp.hasPendingCommands() ) + QCoreApplication::instance()->processEvents(); + + return true; +} + + + +/* + * Enter the dir dir on the server and remove all files (not recursive!) + */ + +void uiLoader::ftpClearDirectory(const QString& pathDir) +{ + qDebug() << "\t(I) Clearing directory remote: " << pathDir; + + ftpList(pathDir); + QList<QString> dirListing(lsDirList); + + QFtp ftp; + ftp.connectToHost( ftpHost ); + ftp.login( ftpUser, ftpPass ); + + for (int i = 0; i < dirListing.size(); ++i) { + QString file = dirListing.at(i); + qDebug() << "\t(I) Removing" << pathDir + file; + ftp.remove(pathDir + file); + } + + ftp.close(); + + while ( ftp.hasPendingCommands() ) + QCoreApplication::instance()->processEvents(); +} + + + +/* + * Get a directory listing from the server in the dir dir. + * You can access it via lsDirList. + */ + +void uiLoader::ftpList(const QString & dir) { + qDebug() << "\t(I) Getting list of files in dir" << dir; + + lsDirList.clear(); + + QFtp ftp; + QObject::connect( &ftp, SIGNAL( listInfo( const QUrlInfo & ) ), this, SLOT( ftpAddLsEntry(const QUrlInfo & ) ) ); + //QObject::connect( &ftp, SIGNAL( done( bool ) ), this, SLOT( ftpAddLsDone( bool ) ) ); + + ftp.connectToHost( ftpHost ); + ftp.login( ftpUser, ftpPass ); + + ftp.list( dir ); + ftp.close(); + + while ( ftp.hasPendingCommands() ) + QCoreApplication::instance()->processEvents(); +} + + + +/* + * Creates a dir on the ftp server. + * + * Hint: If the ftp.mkdir() fails we just assume the dir already exist. + */ + +void uiLoader::ftpMkDir( QString pathDir ) +{ + QFtp ftp; + + QSignalSpy commandSpy(&ftp, SIGNAL(commandFinished(int, bool))); + + ftp.connectToHost( ftpHost ); + ftp.login( ftpUser, ftpPass ); + const int command = ftp.mkdir( pathDir ); + ftp.close(); + + while ( ftp.hasPendingCommands() ) + QCoreApplication::instance()->processEvents(); + + // check wheter there was an error or not + for (int i = 0; i < commandSpy.count(); ++i) { + if (commandSpy.at(i).at(0) == command) { + if ( !commandSpy.at(i).at(1).toBool() ) { + qDebug() << "\t(I) Created at remote machine:" << pathDir; + } else { + qDebug() << "\t(I) Could not create on remote machine - probably the dir exists"; + } + } + } +} + + + +/* + * Just a slot, needed for ftpList(). + */ + +void uiLoader::ftpAddLsEntry( const QUrlInfo &urlInfo ) +{ + //Just adding the file to the list + lsDirList << urlInfo.name(); +} + +/* + * Return a list of the test case ui files + */ + +QStringList uiLoader::uiFiles() const +{ + QString baselinePath = QDir::currentPath(); + baselinePath += QLatin1String("/baseline"); + QDir dir(baselinePath); + dir.setFilter(QDir::Files); + dir.setNameFilters(QStringList(QLatin1String("*.ui"))); + const QFileInfoList list = dir.entryInfoList(); + QStringList rc; + const QChar slash = QLatin1Char('/'); + foreach (const QFileInfo &fi, list) { + QString fileAbsolutePath = baselinePath; + fileAbsolutePath += slash; + fileAbsolutePath += fi.fileName(); + rc.push_back(fileAbsolutePath); + } + return rc; +} +/* + * The actual method for generating local files that will be compared + * to the baseline. + * + * The external program uiscreenshot/uiscreenshot is called to generate + * *.png files of *.ui files. + */ + +void uiLoader::executeTests() +{ + qDebug(" ========== Executing the tests...[generating pngs from uis]"); + + qDebug() << "Current Dir" << QDir::currentPath(); + + qDebug() << "\t(I) Using" << pathToProgram; + + QProcess myProcess; + foreach(const QString &fileAbsolutePath, uiFiles()) { + qDebug() << "\t(I) Current file:" << fileAbsolutePath; + + QHashIterator<QString, QString> j(enginesToTest); + while ( j.hasNext() ) { + j.next(); + + QString outputDirectory = output + '/' + j.key(); + + QStringList arguments; + arguments << fileAbsolutePath; + arguments << outputDirectory; + + myProcess.start(pathToProgram, arguments); + + // took too long? + errorMsg = "process does not exited normally (QProcess timeout) - " + pathToProgram; + QVERIFY2( myProcess.waitForFinished(), qPrintable(errorMsg) ); + + qDebug() << "\n" << myProcess.readAllStandardError(); + + // check exit code/status + errorMsg = "process does not exited normally - " + pathToProgram; + QVERIFY2( myProcess.exitStatus() == QProcess::NormalExit, qPrintable(errorMsg) ); + QVERIFY2( myProcess.exitCode() == EXIT_SUCCESS, qPrintable(errorMsg) ); + } + } +} + +/* + * Comparing generated files to the baseline. + */ + +bool uiLoader::compare() +{ + qDebug( " ========== Now comparing the results to the baseline" ); + + QDir dir(output); + + QHashIterator<QString, QString> i(enginesToTest); + while ( i.hasNext() ) { + i.next(); + + QString engineName = i.key(); + + // Perform comparisons between the two directories. + dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); + dir.setNameFilters( QStringList() << "*.png" ); + dir.cd( engineName + ".baseline" ); + + QFileInfoList list = dir.entryInfoList(); + + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + diff(output, engineName, fileInfo.fileName()); + } + } + + return true; +} + + + + +void uiLoader::diff(const QString& basedir, const QString& engine, const QString& fileName) +{ + QString filePathBaseline = basedir + "/" + engine + ".baseline/" + fileName; + QString filePathGenerated = basedir + "/" + engine + '/' + fileName; + + qDebug() << "\t(I) Comparing" << filePathBaseline; + qDebug() << "\t(I) Comparing" << filePathGenerated; + + QString filePathDiffImage = basedir + "/" + engine + ".diff/" + fileName; + + if ( QFile::exists(filePathGenerated) ) { + QString filePathDiffImage = basedir + "/" + engine + ".diff/" + fileName; + int pixelDiff = imgDiff(filePathBaseline, filePathGenerated, filePathDiffImage); + + if ( pixelDiff <= threshold.toInt() ) { + qDebug() << "\t(I) TEST OK"; + QVERIFY(true); + } else { + qDebug() << "\t(I) TEST FAILED"; + qDebug() << "\t(I)\t...saving baseline in *.failed"; + + // local: save in *.failed + QString filePathFailed = basedir + "/" + engine + ".failed/" + fileName; + errorMsg = "Could not save " + filePathGenerated + " to " + filePathFailed; + QVERIFY2( QFile::copy(filePathGenerated, filePathFailed), qPrintable(errorMsg) ); + + // remote: save in *.failed + QString filePathFailedRemote = ftpBaseDir + "/" + engine + ".failed" + "/" + fileName; + ftpUploadFile(filePathFailedRemote, filePathGenerated); + + errorMsg = "Something broke in the image comparison with " + filePathDiffImage; + QVERIFY2( (pixelDiff != -1), qPrintable(errorMsg) ); + + // remote: save in *.diff + QString filePathDiffRemote = ftpBaseDir + "/" + engine + ".diff" + "/" + fileName; + ftpUploadFile(filePathDiffRemote, filePathDiffImage); + QFAIL(qPrintable(fileName)); + } + + } else { + qWarning() << "\t(W) Expected generated file" << filePathGenerated << "does not exist."; + qWarning() << "\t(W) ...saving baseline in *.failed"; + + // save local + QString filePathMissing = basedir + '/' + engine + ".failed/" + fileName + "_missing"; + errorMsg = "Could not save " + filePathMissing; + QVERIFY2( QFile::copy(filePathBaseline, filePathMissing), qPrintable(errorMsg) ); + + // save remote + QString filePathDiffRemote = ftpBaseDir + "/" + engine + ".diff" + "/" + fileName; + ftpUploadFile(filePathDiffRemote, filePathBaseline); + + errorMsg = filePathGenerated + " was not generated, but baseline for this file exists"; + QVERIFY2(false, qPrintable(errorMsg)); + } + +} + +/* + * Execution starts here. + */ + +uiLoader::TestResult uiLoader::runAutoTests(QString *errorMessage) +{ + // SVG needs this widget... + QWidget dummy; + + qDebug() << "Running test on buildkey:" << QLibraryInfo::buildKey() << " qt version:" << qVersion(); + qDebug() << "Initializing tests..."; + + // load config + const QString configFileName = QHostInfo::localHostName().split(QLatin1Char('.')).first() + QLatin1String(".ini"); + const QFileInfo fi(configFileName); + if (!fi.isFile() || !fi.isReadable()) { + *errorMessage = QString::fromLatin1("Config file '%1' does not exist or is not readable.").arg(configFileName); + return TestNoConfig; + } + + if (!loadConfig(configFileName, errorMessage)) + return TestConfigError; + + // reset the local environment where the results are stored + setupLocal(); + + // reset the FTP environment where the results are stored + setupFTP(); + + // retrieve the latest test result baseline from the FTP server. + downloadBaseline(); + + // execute tests + executeTests(); + + // upload testresults as new baseline or compare results + if ( lsNeedBaseline.size() ) + createBaseline(); + else + compare(); + + return TestRunDone; +} + +int uiLoader::imgDiff(const QString fileA, const QString fileB, const QString output) +{ +// qDebug() << "Comparing " << fileA << " and " << fileB << " outputting to " << output; + QImage imageA(fileA); + QImage imageB(fileB); + + // Invalid images + if (imageA.isNull() || imageB.isNull()) + { + qDebug() << "Fatal error: unable to open one or more input images."; + return false; + } + + //Choose the largest image size, so that the output can capture the entire diff. + QSize largestSize = imageA.size(); + QSize otherSize = imageB.size(); + + if (largestSize.width() < otherSize.width()) + largestSize.setWidth(otherSize.width()); + + if (largestSize.height() < otherSize.height()) + largestSize.setHeight(otherSize.height()); + + QImage imageDiff(largestSize, QImage::Format_ARGB32); + + imageA = imageA.convertToFormat(QImage::Format_ARGB32); + imageB = imageB.convertToFormat(QImage::Format_ARGB32); + + int pixelDiff = 0; + + for (int y = 0; y < imageDiff.height(); ++y) + { + for (int x = 0; x < imageDiff.width(); ++x) + { + //Are the pixels within range? Else, draw a black pixel in diff. + if (imageA.valid(x,y) && imageB.valid(x,y)) + { + //Both images have a pixel at x,y - are they the same? If not, black pixel in diff. + if (imageA.pixel(x,y) != imageB.pixel(x,y)) + { + imageDiff.setPixel(x,y,0xff000000); + pixelDiff++; + } + else + imageDiff.setPixel(x,y,0xffffffff); + } + else + { + imageDiff.setPixel(x,y,0xff000000); + pixelDiff++; + } + } + } + + imageDiff.setText("comment", QString::number(pixelDiff)); + + if (!imageDiff.save(output, "PNG")) + pixelDiff = -1; + + return pixelDiff; +} |