diff -r 4f2f89ce4247 -r 303757a437d3 WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp --- a/WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp Fri Sep 17 09:02:29 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,411 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// This file input format is based loosely on -// WebKitTools/DumpRenderTree/ImageDiff.m - -// The exact format of this tool's output to stdout is important, to match -// what the run-webkit-tests script expects. - -#include "config.h" - -#include "gfx/codec/png_codec.h" -#include -#include -#include -#include -#include -#include - -#if OS(WINDOWS) -#include -#define PATH_MAX MAX_PATH -#endif - -using namespace gfx; -using namespace std; - -// Causes the app to remain open, waiting for pairs of filenames on stdin. -// The caller is then responsible for terminating this app. -static const char optionPollStdin[] = "--use-stdin"; -static const char optionGenerateDiff[] = "--diff"; - -// Return codes used by this utility. -static const int statusSame = 0; -static const int statusDifferent = 1; -static const int statusError = 2; - -// Color codes. -static const uint32_t rgbaRed = 0x000000ff; -static const uint32_t rgbaAlpha = 0xff000000; - -class Image { -public: - Image() - : m_width(0) - , m_height(0) {} - - Image(const Image& image) - : m_width(image.m_width) - , m_height(image.m_height) - , m_data(image.m_data) {} - - bool hasImage() const { return m_width > 0 && m_height > 0; } - int width() const { return m_width; } - int height() const { return m_height; } - const unsigned char* data() const { return &m_data.front(); } - - // Creates the image from stdin with the given data length. On success, it - // will return true. On failure, no other methods should be accessed. - bool craeteFromStdin(size_t byteLength) - { - if (!byteLength) - return false; - - OwnArrayPtr source(new unsigned char[byteLength]); - if (fread(source.get(), 1, byteLength, stdin) != byteLength) - return false; - - if (!PNGCodec::Decode(source.get(), byteLength, PNGCodec::FORMAT_RGBA, - &m_data, &m_width, &m_height)) { - clear(); - return false; - } - return true; - } - - // Creates the image from the given filename on disk, and returns true on - // success. - bool createFromFilename(const char* filename) - { - FILE* f = fopen(filename, "rb"); - if (!f) - return false; - - vector compressed; - const int bufSize = 1024; - unsigned char buf[bufSize]; - size_t numRead = 0; - while ((numRead = fread(buf, 1, bufSize, f)) > 0) - std::copy(buf, &buf[numRead], std::back_inserter(compressed)); - - fclose(f); - - if (!PNGCodec::Decode(&compressed[0], compressed.size(), - PNGCodec::FORMAT_RGBA, &m_data, &m_width, &m_height)) { - clear(); - return false; - } - return true; - } - - void clear() - { - m_width = m_height = 0; - m_data.clear(); - } - - // Returns the RGBA value of the pixel at the given location - const uint32_t pixelAt(int x, int y) const - { - ASSERT(x >= 0 && x < m_width); - ASSERT(y >= 0 && y < m_height); - return *reinterpret_cast(&(m_data[(y * m_width + x) * 4])); - } - - void setPixelAt(int x, int y, uint32_t color) const - { - ASSERT(x >= 0 && x < m_width); - ASSERT(y >= 0 && y < m_height); - void* addr = &const_cast(&m_data.front())[(y * m_width + x) * 4]; - *reinterpret_cast(addr) = color; - } - -private: - // pixel dimensions of the image - int m_width, m_height; - - vector m_data; -}; - -float percentageDifferent(const Image& baseline, const Image& actual) -{ - int w = min(baseline.width(), actual.width()); - int h = min(baseline.height(), actual.height()); - - // Compute pixels different in the overlap - int pixelsDifferent = 0; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - if (baseline.pixelAt(x, y) != actual.pixelAt(x, y)) - pixelsDifferent++; - } - } - - // Count pixels that are a difference in size as also being different - int maxWidth = max(baseline.width(), actual.width()); - int maxHeight = max(baseline.height(), actual.height()); - - // ...pixels off the right side, but not including the lower right corner - pixelsDifferent += (maxWidth - w) * h; - - // ...pixels along the bottom, including the lower right corner - pixelsDifferent += (maxHeight - h) * maxWidth; - - // Like the WebKit ImageDiff tool, we define percentage different in terms - // of the size of the 'actual' bitmap. - float totalPixels = static_cast(actual.width()) * static_cast(actual.height()); - if (!totalPixels) - return 100.0f; // When the bitmap is empty, they are 100% different. - return static_cast(pixelsDifferent) / totalPixels * 100; -} - -void printHelp() -{ - fprintf(stderr, - "Usage:\n" - " ImageDiff \n" - " Compares two files on disk, returning 0 when they are the same\n" - " ImageDiff --use-stdin\n" - " Stays open reading pairs of filenames from stdin, comparing them,\n" - " and sending 0 to stdout when they are the same\n" - " ImageDiff --diff \n" - " Compares two files on disk, outputs an image that visualizes the" - " difference to \n"); - /* For unfinished webkit-like-mode (see below) - "\n" - " ImageDiff -s\n" - " Reads stream input from stdin, should be EXACTLY of the format\n" - " \"Content-length: Content-length: ...\n" - " it will take as many file pairs as given, and will compare them as\n" - " (cmp_file, reference_file) pairs\n"); - */ -} - -int compareImages(const char* file1, const char* file2) -{ - Image actualImage; - Image baselineImage; - - if (!actualImage.createFromFilename(file1)) { - fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file1); - return statusError; - } - if (!baselineImage.createFromFilename(file2)) { - fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file2); - return statusError; - } - - float percent = percentageDifferent(actualImage, baselineImage); - if (percent > 0.0) { - // failure: The WebKit version also writes the difference image to - // stdout, which seems excessive for our needs. - printf("diff: %01.2f%% failed\n", percent); - return statusDifferent; - } - - // success - printf("diff: %01.2f%% passed\n", percent); - return statusSame; - -} - -// Untested mode that acts like WebKit's image comparator. I wrote this but -// decided it's too complicated. We may use it in the future if it looks useful. -int untestedCompareImages() -{ - Image actualImage; - Image baselineImage; - char buffer[2048]; - while (fgets(buffer, sizeof(buffer), stdin)) { - if (!strncmp("Content-length: ", buffer, 16)) { - char* context; -#if OS(WINDOWS) - strtok_s(buffer, " ", &context); - int imageSize = strtol(strtok_s(0, " ", &context), 0, 10); -#else - strtok_r(buffer, " ", &context); - int imageSize = strtol(strtok_r(0, " ", &context), 0, 10); -#endif - - bool success = false; - if (imageSize > 0 && !actualImage.hasImage()) { - if (!actualImage.craeteFromStdin(imageSize)) { - fputs("Error, input image can't be decoded.\n", stderr); - return 1; - } - } else if (imageSize > 0 && !baselineImage.hasImage()) { - if (!baselineImage.craeteFromStdin(imageSize)) { - fputs("Error, baseline image can't be decoded.\n", stderr); - return 1; - } - } else { - fputs("Error, image size must be specified.\n", stderr); - return 1; - } - } - - if (actualImage.hasImage() && baselineImage.hasImage()) { - float percent = percentageDifferent(actualImage, baselineImage); - if (percent > 0.0) { - // failure: The WebKit version also writes the difference image to - // stdout, which seems excessive for our needs. - printf("diff: %01.2f%% failed\n", percent); - } else { - // success - printf("diff: %01.2f%% passed\n", percent); - } - actualImage.clear(); - baselineImage.clear(); - } - fflush(stdout); - } - return 0; -} - -bool createImageDiff(const Image& image1, const Image& image2, Image* out) -{ - int w = min(image1.width(), image2.width()); - int h = min(image1.height(), image2.height()); - *out = Image(image1); - bool same = (image1.width() == image2.width()) && (image1.height() == image2.height()); - - // FIXME: do something with the extra pixels if the image sizes are different. - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - uint32_t basePixel = image1.pixelAt(x, y); - if (basePixel != image2.pixelAt(x, y)) { - // Set differing pixels red. - out->setPixelAt(x, y, rgbaRed | rgbaAlpha); - same = false; - } else { - // Set same pixels as faded. - uint32_t alpha = basePixel & rgbaAlpha; - uint32_t newPixel = basePixel - ((alpha / 2) & rgbaAlpha); - out->setPixelAt(x, y, newPixel); - } - } - } - - return same; -} - -static bool writeFile(const char* outFile, const unsigned char* data, size_t dataSize) -{ - FILE* file = fopen(outFile, "wb"); - if (!file) { - fprintf(stderr, "ImageDiff: Unable to create file \"%s\"\n", file); - return false; - } - if (dataSize != fwrite(data, 1, dataSize, file)) { - fclose(file); - fprintf(stderr, "ImageDiff: Unable to write data to file \"%s\"\n", file); - return false; - } - fclose(file); - return true; -} - -int diffImages(const char* file1, const char* file2, const char* outFile) -{ - Image actualImage; - Image baselineImage; - - if (!actualImage.createFromFilename(file1)) { - fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file1); - return statusError; - } - if (!baselineImage.createFromFilename(file2)) { - fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file2); - return statusError; - } - - Image diffImage; - bool same = createImageDiff(baselineImage, actualImage, &diffImage); - if (same) - return statusSame; - - vector pngData; - PNGCodec::Encode(diffImage.data(), PNGCodec::FORMAT_RGBA, diffImage.width(), - diffImage.height(), diffImage.width() * 4, false, &pngData); - if (!writeFile(outFile, &pngData.front(), pngData.size())) - return statusError; - return statusDifferent; -} - -int main(int argc, const char* argv[]) -{ - Vector values; - bool pollStdin = false; - bool generateDiff = false; - for (int i = 1; i < argc; ++i) { - if (!strcmp(argv[i], optionPollStdin)) - pollStdin = true; - else if (!strcmp(argv[i], optionGenerateDiff)) - generateDiff = true; - else - values.append(argv[i]); - } - - if (pollStdin) { - // Watch stdin for filenames. - const size_t bufferSize = PATH_MAX; - char stdinBuffer[bufferSize]; - char firstName[bufferSize]; - bool haveFirstName = false; - while (fgets(stdinBuffer, bufferSize, stdin)) { - if (!stdinBuffer[0]) - continue; - - if (haveFirstName) { - // compareImages writes results to stdout unless an error occurred. - if (compareImages(firstName, stdinBuffer) == statusError) - printf("error\n"); - fflush(stdout); - haveFirstName = false; - } else { - // Save the first filename in another buffer and wait for the second - // filename to arrive via stdin. - strcpy(firstName, stdinBuffer); - haveFirstName = true; - } - } - return 0; - } - - if (generateDiff) { - if (values.size() == 3) - return diffImages(values[0], values[1], values[2]); - } else if (values.size() == 2) - return compareImages(argv[1], argv[2]); - - printHelp(); - return statusError; -}