1e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// Use of this source code is governed by a BSD-style license that can be 3e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// found in the LICENSE file. 4e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 5e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// This file input format is based loosely on 6e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// Tools/DumpRenderTree/ImageDiff.m 7e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 8e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// The exact format of this tool's output to stdout is important, to match 9e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// what the run-webkit-tests script expects. 10e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 11e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <assert.h> 12e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <stdint.h> 13e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <stdio.h> 14e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <string.h> 15e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 16e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <algorithm> 17e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <iostream> 18e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <map> 19e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <string> 20e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include <vector> 21e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 22e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include "../third_party/base/logging.h" 23e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include "../third_party/base/numerics/safe_conversions.h" 24e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include "image_diff_png.h" 25e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 26e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#if defined(OS_WIN) 27e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#include "windows.h" 28e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov#endif 29e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 30e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// Return codes used by this utility. 31e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovstatic const int kStatusSame = 0; 32e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovstatic const int kStatusDifferent = 1; 33e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovstatic const int kStatusError = 2; 34e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 35e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// Color codes. 36e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovstatic const uint32_t RGBA_RED = 0x000000ff; 37e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovstatic const uint32_t RGBA_ALPHA = 0xff000000; 38e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 39e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovclass Image { 40e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov public: 41e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Image() : w_(0), h_(0) { 42e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 43e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 44e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Image(const Image& image) 45e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov : w_(image.w_), 46e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov h_(image.h_), 47e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov data_(image.data_) { 48e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 49e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 50e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool has_image() const { 51e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return w_ > 0 && h_ > 0; 52e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 53e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 54e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int w() const { 55e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return w_; 56e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 57e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 58e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int h() const { 59e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return h_; 60e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 61e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 62e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const unsigned char* data() const { 63e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return &data_.front(); 64e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 65e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 66e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Creates the image from the given filename on disk, and returns true on 67e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // success. 68e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool CreateFromFilename(const std::string& path) { 69e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov FILE* f = fopen(path.c_str(), "rb"); 70e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!f) 71e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return false; 72e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 73e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov std::vector<unsigned char> compressed; 74e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const int buf_size = 1024; 75e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov unsigned char buf[buf_size]; 76e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov size_t num_read = 0; 77e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov while ((num_read = fread(buf, 1, buf_size, f)) > 0) { 78e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov compressed.insert(compressed.end(), buf, buf + num_read); 79e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 80e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 81e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fclose(f); 82e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 83e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!image_diff_png::DecodePNG(&compressed[0], compressed.size(), 84e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov &data_, &w_, &h_)) { 85e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Clear(); 86e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return false; 87e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 88e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return true; 89e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 90e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 91e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov void Clear() { 92e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov w_ = h_ = 0; 93e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov data_.clear(); 94e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 95e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 96e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Returns the RGBA value of the pixel at the given location 97e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov uint32_t pixel_at(int x, int y) const { 98e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (x >= 0 && x < w_ && y >= 0 && y < h_) 99e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return *reinterpret_cast<const uint32_t*>(&(data_[(y * w_ + x) * 4])); 100e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 0; 101e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 102e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 103e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov void set_pixel_at(int x, int y, uint32_t color) const { 104e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (x >= 0 && x < w_ && y >= 0 && y < h_) { 105e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov void* addr = &const_cast<unsigned char*>(&data_.front())[(y * w_ + x) * 4]; 106e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov *reinterpret_cast<uint32_t*>(addr) = color; 107e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 108e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 109e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 110e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov private: 111e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // pixel dimensions of the image 112e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int w_, h_; 113e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 114e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov std::vector<unsigned char> data_; 115e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov}; 116e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 117e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovfloat PercentageDifferent(const Image& baseline, const Image& actual) { 118e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int w = std::min(baseline.w(), actual.w()); 119e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int h = std::min(baseline.h(), actual.h()); 120e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 121e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Compute pixels different in the overlap. 122e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int pixels_different = 0; 123e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int y = 0; y < h; y++) { 124e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int x = 0; x < w; x++) { 125e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (baseline.pixel_at(x, y) != actual.pixel_at(x, y)) 126e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov pixels_different++; 127e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 128e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 129e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 130e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Count pixels that are a difference in size as also being different. 131e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int max_w = std::max(baseline.w(), actual.w()); 132e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int max_h = std::max(baseline.h(), actual.h()); 133e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // These pixels are off the right side, not including the lower right corner. 134e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov pixels_different += (max_w - w) * h; 135e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // These pixels are along the bottom, including the lower right corner. 136e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov pixels_different += (max_h - h) * max_w; 137e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 138e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Like the WebKit ImageDiff tool, we define percentage different in terms 139e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // of the size of the 'actual' bitmap. 140e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov float total_pixels = static_cast<float>(actual.w()) * 141e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov static_cast<float>(actual.h()); 142e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (total_pixels == 0) { 143e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // When the bitmap is empty, they are 100% different. 144e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 100.0f; 145e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 146e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 100.0f * pixels_different / total_pixels; 147e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov} 148e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 149e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov// FIXME: Replace with unordered_map when available. 150e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovtypedef std::map<uint32_t, int32_t> RgbaToCountMap; 151e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 152e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovfloat HistogramPercentageDifferent(const Image& baseline, const Image& actual) { 153e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // TODO(johnme): Consider using a joint histogram instead, as described in 154e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // "Comparing Images Using Joint Histograms" by Pass & Zabih 155e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // http://www.cs.cornell.edu/~rdz/papers/pz-jms99.pdf 156e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 157e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int w = std::min(baseline.w(), actual.w()); 158e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int h = std::min(baseline.h(), actual.h()); 159e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 160e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Count occurences of each RGBA pixel value of baseline in the overlap. 161e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov RgbaToCountMap baseline_histogram; 162e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int y = 0; y < h; y++) { 163e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int x = 0; x < w; x++) { 164e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // hash_map operator[] inserts a 0 (default constructor) if key not found. 165e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov baseline_histogram[baseline.pixel_at(x, y)]++; 166e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 167e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 168e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 169e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Compute pixels different in the histogram of the overlap. 170e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int pixels_different = 0; 171e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int y = 0; y < h; y++) { 172e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int x = 0; x < w; x++) { 173e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov uint32_t actual_rgba = actual.pixel_at(x, y); 174e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov RgbaToCountMap::iterator it = baseline_histogram.find(actual_rgba); 175e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (it != baseline_histogram.end() && it->second > 0) 176e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov it->second--; 177e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov else 178e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov pixels_different++; 179e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 180e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 181e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 182e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Count pixels that are a difference in size as also being different. 183e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int max_w = std::max(baseline.w(), actual.w()); 184e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int max_h = std::max(baseline.h(), actual.h()); 185e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // These pixels are off the right side, not including the lower right corner. 186e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov pixels_different += (max_w - w) * h; 187e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // These pixels are along the bottom, including the lower right corner. 188e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov pixels_different += (max_h - h) * max_w; 189e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 190e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Like the WebKit ImageDiff tool, we define percentage different in terms 191e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // of the size of the 'actual' bitmap. 192e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov float total_pixels = static_cast<float>(actual.w()) * 193e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov static_cast<float>(actual.h()); 194e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (total_pixels == 0) { 195e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // When the bitmap is empty, they are 100% different. 196e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 100.0f; 197e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 198e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 100.0f * pixels_different / total_pixels; 199e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov} 200e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 201e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovvoid PrintHelp() { 202e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fprintf(stderr, 203e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov "Usage:\n" 204e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov " image_diff [--histogram] <compare file> <reference file>\n" 205e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov " Compares two files on disk, returning 0 when they are the same;\n" 206e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov " passing \"--histogram\" additionally calculates a diff of the\n" 207e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov " RGBA value histograms (which is resistant to shifts in layout)\n" 208e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov " image_diff --diff <compare file> <reference file> <output file>\n" 209e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov " Compares two files on disk, outputs an image that visualizes the\n" 210e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov " difference to <output file>\n"); 211e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov} 212e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 213e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovint CompareImages(const std::string& file1, 214e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const std::string& file2, 215e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool compare_histograms) { 216e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Image actual_image; 217e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Image baseline_image; 218e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 219e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!actual_image.CreateFromFilename(file1)) { 220e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file1.c_str()); 221e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusError; 222e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 223e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!baseline_image.CreateFromFilename(file2)) { 224e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file2.c_str()); 225e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusError; 226e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 227e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 228e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (compare_histograms) { 229e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov float percent = HistogramPercentageDifferent(actual_image, baseline_image); 230e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const char* passed = percent > 0.0 ? "failed" : "passed"; 231e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov printf("histogram diff: %01.2f%% %s\n", percent, passed); 232e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 233e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 234e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const char* diff_name = compare_histograms ? "exact diff" : "diff"; 235e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov float percent = PercentageDifferent(actual_image, baseline_image); 236e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const char* passed = percent > 0.0 ? "failed" : "passed"; 237e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov printf("%s: %01.2f%% %s\n", diff_name, percent, passed); 238e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (percent > 0.0) { 239e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // failure: The WebKit version also writes the difference image to 240e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // stdout, which seems excessive for our needs. 241e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusDifferent; 242e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 243e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // success 244e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusSame; 245e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 246e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov/* Untested mode that acts like WebKit's image comparator. I wrote this but 247e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov decided it's too complicated. We may use it in the future if it looks useful 248e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 249e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov char buffer[2048]; 250e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov while (fgets(buffer, sizeof(buffer), stdin)) { 251e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 252e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (strncmp("Content-length: ", buffer, 16) == 0) { 253e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov char* context; 254e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov strtok_s(buffer, " ", &context); 255e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int image_size = strtol(strtok_s(NULL, " ", &context), NULL, 10); 256e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 257e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool success = false; 258e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (image_size > 0 && actual_image.has_image() == 0) { 259e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!actual_image.CreateFromStdin(image_size)) { 260e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fputs("Error, input image can't be decoded.\n", stderr); 261e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 1; 262e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 263e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } else if (image_size > 0 && baseline_image.has_image() == 0) { 264e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!baseline_image.CreateFromStdin(image_size)) { 265e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fputs("Error, baseline image can't be decoded.\n", stderr); 266e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 1; 267e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 268e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } else { 269e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fputs("Error, image size must be specified.\n", stderr); 270e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return 1; 271e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 272e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 273e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 274e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (actual_image.has_image() && baseline_image.has_image()) { 275e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov float percent = PercentageDifferent(actual_image, baseline_image); 276e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (percent > 0.0) { 277e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // failure: The WebKit version also writes the difference image to 278e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // stdout, which seems excessive for our needs. 279e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov printf("diff: %01.2f%% failed\n", percent); 280e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } else { 281e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // success 282e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov printf("diff: %01.2f%% passed\n", percent); 283e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 284e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov actual_image.Clear(); 285e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov baseline_image.Clear(); 286e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 287e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 288e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fflush(stdout); 289e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 290e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov*/ 291e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov} 292e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 293e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovbool CreateImageDiff(const Image& image1, const Image& image2, Image* out) { 294e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int w = std::min(image1.w(), image2.w()); 295e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int h = std::min(image1.h(), image2.h()); 296e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov *out = Image(image1); 297e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool same = (image1.w() == image2.w()) && (image1.h() == image2.h()); 298e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 299e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // TODO(estade): do something with the extra pixels if the image sizes 300e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // are different. 301e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int y = 0; y < h; y++) { 302e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (int x = 0; x < w; x++) { 303e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov uint32_t base_pixel = image1.pixel_at(x, y); 304e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (base_pixel != image2.pixel_at(x, y)) { 305e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Set differing pixels red. 306e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov out->set_pixel_at(x, y, RGBA_RED | RGBA_ALPHA); 307e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov same = false; 308e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } else { 309e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov // Set same pixels as faded. 310e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov uint32_t alpha = base_pixel & RGBA_ALPHA; 311e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov uint32_t new_pixel = base_pixel - ((alpha / 2) & RGBA_ALPHA); 312e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov out->set_pixel_at(x, y, new_pixel); 313e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 314e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 315e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 316e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 317e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return same; 318e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov} 319e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 320e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovint DiffImages(const std::string& file1, 321e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const std::string& file2, 322e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const std::string& out_file) { 323e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Image actual_image; 324e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Image baseline_image; 325e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 326e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!actual_image.CreateFromFilename(file1)) { 327e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file1.c_str()); 328e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusError; 329e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 330e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!baseline_image.CreateFromFilename(file2)) { 331e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file2.c_str()); 332e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusError; 333e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 334e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 335e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov Image diff_image; 336e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool same = CreateImageDiff(baseline_image, actual_image, &diff_image); 337e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (same) 338e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusSame; 339e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 340e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov std::vector<unsigned char> png_encoding; 341e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov image_diff_png::EncodeRGBAPNG( 342e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov diff_image.data(), diff_image.w(), diff_image.h(), 343e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov diff_image.w() * 4, &png_encoding); 344e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 345e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov FILE *f = fopen(out_file.c_str(), "wb"); 346e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!f) 347e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusError; 348e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 349e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov size_t size = png_encoding.size(); 350e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov char *ptr = reinterpret_cast<char*>(&png_encoding.front()); 351e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (fwrite(ptr, 1, size, f) != size) 352e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusError; 353e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 354e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusDifferent; 355e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov} 356e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 357e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganovint main(int argc, const char* argv[]) { 358e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool histograms = false; 359e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov bool produce_diff_image = false; 360e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov std::string filename1; 361e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov std::string filename2; 362e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov std::string diff_filename; 363e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 364e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov int i; 365e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov for (i = 1; i < argc; ++i) { 366e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov const char* arg = argv[i]; 367e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (strstr(arg, "--") != arg) 368e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov break; 369e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (strcmp(arg, "--histogram") == 0) { 370e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov histograms = true; 371e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } else if (strcmp(arg, "--diff") == 0) { 372e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov produce_diff_image = true; 373e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 374e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 375e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (i < argc) { 376e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov filename1 = argv[i]; 377e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov ++i; 378e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 379e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (i < argc) { 380e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov filename2 = argv[i]; 381e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov ++i; 382e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 383e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (i < argc) { 384e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov diff_filename = argv[i]; 385e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov ++i; 386e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 387e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 388e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (produce_diff_image) { 389e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov if (!diff_filename.empty()) { 390e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return DiffImages(filename1, filename2, diff_filename); 391e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 392e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } else if (!filename2.empty()) { 393e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return CompareImages(filename1, filename2, histograms); 394e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov } 395e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov 396e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov PrintHelp(); 397e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov return kStatusError; 398e6986e1e8d4a57987f47c215490cb080a65ee29aSvet Ganov} 399