ImageDiff.cpp revision 231d4e3152a9c27a73b6ac7badbe6be673aa3ddf
10bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch/*
20bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
30bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
40bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    This library is free software; you can redistribute it and/or
50bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    modify it under the terms of the GNU Library General Public
60bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    License as published by the Free Software Foundation; either
70bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    version 2 of the License, or (at your option) any later version.
80bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
90bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    This library is distributed in the hope that it will be useful,
100bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    but WITHOUT ANY WARRANTY; without even the implied warranty of
110bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    Library General Public License for more details.
130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
140bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    You should have received a copy of the GNU Library General Public License
150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    along with this library; see the file COPYING.LIB.  If not, write to
160bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    Boston, MA 02110-1301, USA.
180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch*/
190bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QApplication>
210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QBuffer>
220bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QByteArray>
230bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QImage>
240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QStringList>
250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <math.h>
270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <stdio.h>
280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
290bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochint main(int argc, char* argv[])
300bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{
310bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QCoreApplication app(argc, argv);
320bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    qreal tolerance = 0;
340bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
350bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QStringList args = app.arguments();
360bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    for (int i = 0; i < argc; ++i)
370bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (args[i] == "-t" || args[i] == "--tolerance")
380bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            tolerance = args[i + 1].toDouble();
390bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    char buffer[2048];
410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QImage actualImage;
420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QImage baselineImage;
430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
440bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    while (fgets(buffer, sizeof(buffer), stdin)) {
450bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        // remove the CR
460bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        char* newLineCharacter = strchr(buffer, '\n');
470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (newLineCharacter)
480bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            *newLineCharacter = '\0';
490bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (!strncmp("Content-Length: ", buffer, 16)) {
510bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            strtok(buffer, " ");
520bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            int imageSize = strtol(strtok(0, " "), 0, 10);
530bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
540bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            if (imageSize <= 0) {
550bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fputs("error, image size must be specified.\n", stdout);
560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            } else {
570bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                unsigned char buffer[2048];
580bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                QBuffer data;
590bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                // Read all the incoming chunks
610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                data.open(QBuffer::WriteOnly);
620bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                while (imageSize > 0) {
630bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    size_t bytesToRead = qMin(imageSize, 2048);
640bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    size_t bytesRead = fread(buffer, 1, bytesToRead, stdin);
650bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    data.write(reinterpret_cast<const char*>(buffer), bytesRead);
660bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    imageSize -= static_cast<int>(bytesRead);
670bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
680bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
690bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                // Convert into QImage
700bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                QImage decodedImage;
710bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                decodedImage.loadFromData(data.data(), "PNG");
720bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                decodedImage.convertToFormat(QImage::Format_ARGB32);
730bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
740bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                // Place it in the right place
750bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (actualImage.isNull())
760bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    actualImage = decodedImage;
770bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                else
780bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    baselineImage = decodedImage;
790bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            }
800bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        }
810bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
820bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (!actualImage.isNull() && !baselineImage.isNull()) {
830bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
840bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            if (actualImage.size() != baselineImage.size()) {
850bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fprintf(stderr, "error, test and reference image have different properties.\n");
860bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fflush(stderr);
870bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            } else {
880bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
890bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                int w = actualImage.width();
900bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                int h = actualImage.height();
910bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                QImage diffImage(w, h, QImage::Format_ARGB32);
920bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
930bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                int count = 0;
940bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                qreal sum = 0;
950bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                qreal maxDistance = 0;
960bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
970bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                for (int x = 0; x < w; ++x)
980bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    for (int y = 0; y < h; ++y) {
990bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        QRgb pixel = actualImage.pixel(x, y);
1000bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        QRgb basePixel = baselineImage.pixel(x, y);
1010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal red = (qRed(pixel) - qRed(basePixel)) / static_cast<float>(qMax(255 - qRed(basePixel), qRed(basePixel)));
1020bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal green = (qGreen(pixel) - qGreen(basePixel)) / static_cast<float>(qMax(255 - qGreen(basePixel), qGreen(basePixel)));
1030bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal blue = (qBlue(pixel) - qBlue(basePixel)) / static_cast<float>(qMax(255 - qBlue(basePixel), qBlue(basePixel)));
1040bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal alpha = (qAlpha(pixel) - qAlpha(basePixel)) / static_cast<float>(qMax(255 - qAlpha(basePixel), qAlpha(basePixel)));
1050bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal distance = sqrt(red * red + green * green + blue * blue + alpha * alpha) / 2.0f;
1060bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        int gray = distance * qreal(255);
1070bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        diffImage.setPixel(x, y, qRgb(gray, gray, gray));
1080bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        if (distance >= 1 / qreal(255)) {
1090bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                            count++;
1100bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                            sum += distance;
1110bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                            maxDistance = qMax(maxDistance, distance);
1120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        }
1130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
1140bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                qreal difference = 0;
1160bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (count)
1170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    difference = 100 * sum / static_cast<qreal>(w * h);
1180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (difference <= tolerance) {
1190bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    difference = 0;
1200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                } else {
1210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    difference = round(difference * 100) / 100;
122231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block                    difference = qMax(difference, qreal(0.01));
1230bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
1240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (!count) {
1260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    fprintf(stdout, "diff: %01.2f%% passed\n", difference);
1270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                } else {
1280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    QBuffer buffer;
1290bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    buffer.open(QBuffer::WriteOnly);
1300bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    diffImage.save(&buffer, "PNG");
1310bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    buffer.close();
1320bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    const QByteArray &data = buffer.data();
1330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    printf("Content-Length: %lu\n", static_cast<unsigned long>(data.length()));
1340bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    fwrite(data.constData(), 1, data.length(), stdout);
1350bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1360bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    fprintf(stdout, "diff: %01.2f%% failed\n", difference);
1370bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
1380bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1390bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fflush(stdout);
1400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            }
1410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        }
1420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    }
1430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1440bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    return 0;
1450bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch}
146