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
206c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen#include <QtCore/qmath.h>
216c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen
220bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QApplication>
230bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QBuffer>
240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QByteArray>
250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QImage>
260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <QStringList>
270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#include <stdio.h>
290bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
300bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochint main(int argc, char* argv[])
310bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{
320bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QCoreApplication app(argc, argv);
330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
340bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    qreal tolerance = 0;
350bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
360bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QStringList args = app.arguments();
370bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    for (int i = 0; i < argc; ++i)
380bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (args[i] == "-t" || args[i] == "--tolerance")
390bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            tolerance = args[i + 1].toDouble();
400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    char buffer[2048];
420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QImage actualImage;
430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    QImage baselineImage;
440bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
450bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    while (fgets(buffer, sizeof(buffer), stdin)) {
460bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        // remove the CR
470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        char* newLineCharacter = strchr(buffer, '\n');
480bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (newLineCharacter)
490bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            *newLineCharacter = '\0';
500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
510bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (!strncmp("Content-Length: ", buffer, 16)) {
520bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            strtok(buffer, " ");
530bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            int imageSize = strtol(strtok(0, " "), 0, 10);
540bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
550bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            if (imageSize <= 0) {
560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fputs("error, image size must be specified.\n", stdout);
570bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            } else {
580bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                unsigned char buffer[2048];
590bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                QBuffer data;
600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                // Read all the incoming chunks
620bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                data.open(QBuffer::WriteOnly);
630bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                while (imageSize > 0) {
640bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    size_t bytesToRead = qMin(imageSize, 2048);
650bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    size_t bytesRead = fread(buffer, 1, bytesToRead, stdin);
660bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    data.write(reinterpret_cast<const char*>(buffer), bytesRead);
670bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    imageSize -= static_cast<int>(bytesRead);
680bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
690bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
700bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                // Convert into QImage
710bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                QImage decodedImage;
720bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                decodedImage.loadFromData(data.data(), "PNG");
730bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                decodedImage.convertToFormat(QImage::Format_ARGB32);
740bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
750bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                // Place it in the right place
760bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (actualImage.isNull())
770bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    actualImage = decodedImage;
780bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                else
790bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    baselineImage = decodedImage;
800bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            }
810bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        }
820bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
830bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        if (!actualImage.isNull() && !baselineImage.isNull()) {
840bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
850bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            if (actualImage.size() != baselineImage.size()) {
860bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fprintf(stderr, "error, test and reference image have different properties.\n");
870bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fflush(stderr);
880bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            } else {
890bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
900bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                int w = actualImage.width();
910bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                int h = actualImage.height();
920bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                QImage diffImage(w, h, QImage::Format_ARGB32);
930bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
940bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                int count = 0;
950bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                qreal sum = 0;
960bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                qreal maxDistance = 0;
970bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
980bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                for (int x = 0; x < w; ++x)
990bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    for (int y = 0; y < h; ++y) {
1000bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        QRgb pixel = actualImage.pixel(x, y);
1010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        QRgb basePixel = baselineImage.pixel(x, y);
1020bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal red = (qRed(pixel) - qRed(basePixel)) / static_cast<float>(qMax(255 - qRed(basePixel), qRed(basePixel)));
1030bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal green = (qGreen(pixel) - qGreen(basePixel)) / static_cast<float>(qMax(255 - qGreen(basePixel), qGreen(basePixel)));
1040bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal blue = (qBlue(pixel) - qBlue(basePixel)) / static_cast<float>(qMax(255 - qBlue(basePixel), qBlue(basePixel)));
1050bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        qreal alpha = (qAlpha(pixel) - qAlpha(basePixel)) / static_cast<float>(qMax(255 - qAlpha(basePixel), qAlpha(basePixel)));
1066c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen                        qreal distance = qSqrt(red * red + green * green + blue * blue + alpha * alpha) / 2.0f;
1070bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        int gray = distance * qreal(255);
1080bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        diffImage.setPixel(x, y, qRgb(gray, gray, gray));
1090bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        if (distance >= 1 / qreal(255)) {
1100bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                            count++;
1110bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                            sum += distance;
1120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                            maxDistance = qMax(maxDistance, distance);
1130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                        }
1140bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
1150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1160bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                qreal difference = 0;
1170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (count)
1180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    difference = 100 * sum / static_cast<qreal>(w * h);
1190bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (difference <= tolerance) {
1200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    difference = 0;
1210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                } else {
1226c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen                    difference = qRound(difference * 100) / 100;
123231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block                    difference = qMax(difference, qreal(0.01));
1240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
1250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                if (!count) {
1270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    fprintf(stdout, "diff: %01.2f%% passed\n", difference);
1280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                } else {
1290bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    QBuffer buffer;
1300bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    buffer.open(QBuffer::WriteOnly);
1310bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    diffImage.save(&buffer, "PNG");
1320bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    buffer.close();
1330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    const QByteArray &data = buffer.data();
1340bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    printf("Content-Length: %lu\n", static_cast<unsigned long>(data.length()));
1355abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
1365abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick                    // We have to use the return value of fwrite to avoid "ignoring return value" gcc warning
1375abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick                    // See https://bugs.webkit.org/show_bug.cgi?id=45384 for details.
1385abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick                    if (fwrite(data.constData(), 1, data.length(), stdout)) {}
1390bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                    fprintf(stdout, "diff: %01.2f%% failed\n", difference);
1410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                }
1420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch                fflush(stdout);
1440bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            }
14581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            actualImage = QImage();
14681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            baselineImage = QImage();
1470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        }
1480bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    }
1490bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
1500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    return 0;
1510bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch}
152