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