WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp
changeset 2 303757a437d3
parent 0 4f2f89ce4247
--- 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 <algorithm>
-#include <stdio.h>
-#include <string.h>
-#include <vector>
-#include <wtf/OwnArrayPtr.h>
-#include <wtf/Vector.h>
-
-#if OS(WINDOWS)
-#include <windows.h>
-#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<unsigned char> 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<unsigned char> 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<const uint32_t*>(&(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<unsigned char*>(&m_data.front())[(y * m_width + x) * 4];
-        *reinterpret_cast<uint32_t*>(addr) = color;
-    }
-
-private:
-    // pixel dimensions of the image
-    int m_width, m_height;
-
-    vector<unsigned char> 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<float>(actual.width()) * static_cast<float>(actual.height());
-    if (!totalPixels)
-        return 100.0f; // When the bitmap is empty, they are 100% different.
-    return static_cast<float>(pixelsDifferent) / totalPixels * 100;
-}
-
-void printHelp()
-{
-    fprintf(stderr,
-            "Usage:\n"
-            "  ImageDiff <compare file> <reference file>\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 <compare file> <reference file> <output file>\n"
-            "    Compares two files on disk, outputs an image that visualizes the"
-            "    difference to <output file>\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: <byte length> <data>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<unsigned char> 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<const char*> 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;
-}