15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file input format is based loosely on 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Tools/DumpRenderTree/ImageDiff.m 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The exact format of this tool's output to stdout is important, to match 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// what the run-webkit-tests script expects. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm> 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <iostream> 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <string> 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <vector> 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h" 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/containers/hash_tables.h" 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h" 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/numerics/safe_conversions.h" 24a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/memory.h" 255e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h" 26868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 272385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch#include "tools/imagediff/image_diff_png.h" 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "windows.h" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Causes the app to remain open, waiting for pairs of filenames on stdin. 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The caller is then responsible for terminating this app. 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kOptionPollStdin[] = "use-stdin"; 36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Causes the app to additionally calculate a diff of the color histograms 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// (which is resistant to shifts in layout). 38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)static const char kOptionCompareHistograms[] = "histogram"; 39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Causes the app to output an image that visualizes the difference. 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kOptionGenerateDiff[] = "diff"; 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return codes used by this utility. 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kStatusSame = 0; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kStatusDifferent = 1; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kStatusError = 2; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Color codes. 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const uint32 RGBA_RED = 0x000000ff; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const uint32 RGBA_ALPHA = 0xff000000; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Image { 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Image() : w_(0), h_(0) { 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Image(const Image& image) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : w_(image.w_), 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) h_(image.h_), 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data_(image.data_) { 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool has_image() const { 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return w_ > 0 && h_ > 0; 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int w() const { 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return w_; 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int h() const { 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return h_; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const unsigned char* data() const { 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return &data_.front(); 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Creates the image from stdin with the given data length. On success, it 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // will return true. On failure, no other methods should be accessed. 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool CreateFromStdin(size_t byte_length) { 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (byte_length == 0) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) scoped_ptr<unsigned char[]> source(new unsigned char[byte_length]); 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (fread(source.get(), 1, byte_length, stdin) != byte_length) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 882385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch if (!image_diff_png::DecodePNG(source.get(), byte_length, 8958e6fbe4ee35d65e14b626c557d37565bf8ad179Ben Murdoch &data_, &w_, &h_)) { 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Clear(); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Creates the image from the given filename on disk, and returns true on 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // success. 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool CreateFromFilename(const base::FilePath& path) { 99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) FILE* f = base::OpenFile(path, "rb"); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!f) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<unsigned char> compressed; 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int buf_size = 1024; 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unsigned char buf[buf_size]; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t num_read = 0; 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while ((num_read = fread(buf, 1, buf_size, f)) > 0) { 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) compressed.insert(compressed.end(), buf, buf + num_read); 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::CloseFile(f); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1132385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch if (!image_diff_png::DecodePNG(&compressed[0], compressed.size(), 11458e6fbe4ee35d65e14b626c557d37565bf8ad179Ben Murdoch &data_, &w_, &h_)) { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Clear(); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void Clear() { 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) w_ = h_ = 0; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data_.clear(); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Returns the RGBA value of the pixel at the given location 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint32 pixel_at(int x, int y) const { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(x >= 0 && x < w_); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(y >= 0 && y < h_); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return *reinterpret_cast<const uint32*>(&(data_[(y * w_ + x) * 4])); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void set_pixel_at(int x, int y, uint32 color) const { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(x >= 0 && x < w_); 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(y >= 0 && y < h_); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void* addr = &const_cast<unsigned char*>(&data_.front())[(y * w_ + x) * 4]; 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *reinterpret_cast<uint32*>(addr) = color; 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // pixel dimensions of the image 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int w_, h_; 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<unsigned char> data_; 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)float PercentageDifferent(const Image& baseline, const Image& actual) { 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int w = std::min(baseline.w(), actual.w()); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int h = std::min(baseline.h(), actual.h()); 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Compute pixels different in the overlap. 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int pixels_different = 0; 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int y = 0; y < h; y++) { 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int x = 0; x < w; x++) { 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (baseline.pixel_at(x, y) != actual.pixel_at(x, y)) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixels_different++; 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 160f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Count pixels that are a difference in size as also being different. 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int max_w = std::max(baseline.w(), actual.w()); 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int max_h = std::max(baseline.h(), actual.h()); 163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // These pixels are off the right side, not including the lower right corner. 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixels_different += (max_w - w) * h; 165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // These pixels are along the bottom, including the lower right corner. 166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) pixels_different += (max_h - h) * max_w; 167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Like the WebKit ImageDiff tool, we define percentage different in terms 169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // of the size of the 'actual' bitmap. 170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) float total_pixels = static_cast<float>(actual.w()) * 171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) static_cast<float>(actual.h()); 172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (total_pixels == 0) { 173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // When the bitmap is empty, they are 100% different. 174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return 100.0f; 175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return 100.0f * pixels_different / total_pixels; 177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)typedef base::hash_map<uint32, int32> RgbaToCountMap; 180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)float HistogramPercentageDifferent(const Image& baseline, const Image& actual) { 182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // TODO(johnme): Consider using a joint histogram instead, as described in 183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // "Comparing Images Using Joint Histograms" by Pass & Zabih 184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // http://www.cs.cornell.edu/~rdz/papers/pz-jms99.pdf 185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int w = std::min(baseline.w(), actual.w()); 187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int h = std::min(baseline.h(), actual.h()); 188f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 189f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Count occurences of each RGBA pixel value of baseline in the overlap. 190f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) RgbaToCountMap baseline_histogram; 191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for (int y = 0; y < h; y++) { 192f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for (int x = 0; x < w; x++) { 193f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // hash_map operator[] inserts a 0 (default constructor) if key not found. 194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) baseline_histogram[baseline.pixel_at(x, y)]++; 195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 197f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Compute pixels different in the histogram of the overlap. 199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int pixels_different = 0; 200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for (int y = 0; y < h; y++) { 201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for (int x = 0; x < w; x++) { 202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) uint32 actual_rgba = actual.pixel_at(x, y); 203f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) RgbaToCountMap::iterator it = baseline_histogram.find(actual_rgba); 204f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (it != baseline_histogram.end() && it->second > 0) 205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) it->second--; 206f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) else 207f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) pixels_different++; 208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 209f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 210f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 211f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Count pixels that are a difference in size as also being different. 212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int max_w = std::max(baseline.w(), actual.w()); 213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int max_h = std::max(baseline.h(), actual.h()); 214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // These pixels are off the right side, not including the lower right corner. 215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) pixels_different += (max_w - w) * h; 216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // These pixels are along the bottom, including the lower right corner. 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pixels_different += (max_h - h) * max_w; 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Like the WebKit ImageDiff tool, we define percentage different in terms 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // of the size of the 'actual' bitmap. 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) float total_pixels = static_cast<float>(actual.w()) * 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<float>(actual.h()); 223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (total_pixels == 0) { 224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // When the bitmap is empty, they are 100% different. 225f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return 100.0f; 226f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return 100.0f * pixels_different / total_pixels; 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PrintHelp() { 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Usage:\n" 233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " image_diff [--histogram] <compare file> <reference file>\n" 234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " Compares two files on disk, returning 0 when they are the same;\n" 235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " passing \"--histogram\" additionally calculates a diff of the\n" 236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " RGBA value histograms (which is resistant to shifts in layout)\n" 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " image_diff --use-stdin\n" 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " Stays open reading pairs of filenames from stdin, comparing them,\n" 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " and sending 0 to stdout when they are the same\n" 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " image_diff --diff <compare file> <reference file> <output file>\n" 241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " Compares two files on disk, outputs an image that visualizes the\n" 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " difference to <output file>\n"); 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* For unfinished webkit-like-mode (see below) 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "\n" 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " image_diff -s\n" 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " Reads stream input from stdin, should be EXACTLY of the format\n" 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " \"Content-length: <byte length> <data>Content-length: ...\n" 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " it will take as many file pairs as given, and will compare them as\n" 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " (cmp_file, reference_file) pairs\n"); 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 253f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)int CompareImages(const base::FilePath& file1, 254f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const base::FilePath& file2, 255f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) bool compare_histograms) { 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Image actual_image; 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Image baseline_image; 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!actual_image.CreateFromFilename(file1)) { 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file1.value().c_str()); 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusError; 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!baseline_image.CreateFromFilename(file2)) { 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file2.value().c_str()); 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusError; 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (compare_histograms) { 271f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) float percent = HistogramPercentageDifferent(actual_image, baseline_image); 272f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const char* passed = percent > 0.0 ? "failed" : "passed"; 273f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) printf("histogram diff: %01.2f%% %s\n", percent, passed); 274f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 275f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 276f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const char* diff_name = compare_histograms ? "exact diff" : "diff"; 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) float percent = PercentageDifferent(actual_image, baseline_image); 278f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const char* passed = percent > 0.0 ? "failed" : "passed"; 279f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) printf("%s: %01.2f%% %s\n", diff_name, percent, passed); 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (percent > 0.0) { 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // failure: The WebKit version also writes the difference image to 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // stdout, which seems excessive for our needs. 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusDifferent; 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // success 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusSame; 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Untested mode that acts like WebKit's image comparator. I wrote this but 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) decided it's too complicated. We may use it in the future if it looks useful 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) char buffer[2048]; 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (fgets(buffer, sizeof(buffer), stdin)) { 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (strncmp("Content-length: ", buffer, 16) == 0) { 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) char* context; 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) strtok_s(buffer, " ", &context); 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int image_size = strtol(strtok_s(NULL, " ", &context), NULL, 10); 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool success = false; 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (image_size > 0 && actual_image.has_image() == 0) { 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!actual_image.CreateFromStdin(image_size)) { 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fputs("Error, input image can't be decoded.\n", stderr); 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (image_size > 0 && baseline_image.has_image() == 0) { 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!baseline_image.CreateFromStdin(image_size)) { 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fputs("Error, baseline image can't be decoded.\n", stderr); 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fputs("Error, image size must be specified.\n", stderr); 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (actual_image.has_image() && baseline_image.has_image()) { 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) float percent = PercentageDifferent(actual_image, baseline_image); 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (percent > 0.0) { 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // failure: The WebKit version also writes the difference image to 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // stdout, which seems excessive for our needs. 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) printf("diff: %01.2f%% failed\n", percent); 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // success 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) printf("diff: %01.2f%% passed\n", percent); 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) actual_image.Clear(); 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) baseline_image.Clear(); 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fflush(stdout); 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/ 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CreateImageDiff(const Image& image1, const Image& image2, Image* out) { 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int w = std::min(image1.w(), image2.w()); 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int h = std::min(image1.h(), image2.h()); 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *out = Image(image1); 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool same = (image1.w() == image2.w()) && (image1.h() == image2.h()); 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(estade): do something with the extra pixels if the image sizes 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // are different. 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int y = 0; y < h; y++) { 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int x = 0; x < w; x++) { 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint32 base_pixel = image1.pixel_at(x, y); 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (base_pixel != image2.pixel_at(x, y)) { 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set differing pixels red. 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out->set_pixel_at(x, y, RGBA_RED | RGBA_ALPHA); 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) same = false; 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set same pixels as faded. 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint32 alpha = base_pixel & RGBA_ALPHA; 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint32 new_pixel = base_pixel - ((alpha / 2) & RGBA_ALPHA); 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out->set_pixel_at(x, y, new_pixel); 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return same; 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int DiffImages(const base::FilePath& file1, const base::FilePath& file2, 3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& out_file) { 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Image actual_image; 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Image baseline_image; 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!actual_image.CreateFromFilename(file1)) { 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file1.value().c_str()); 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusError; 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!baseline_image.CreateFromFilename(file2)) { 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file2.value().c_str()); 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusError; 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Image diff_image; 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool same = CreateImageDiff(baseline_image, actual_image, &diff_image); 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (same) 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusSame; 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<unsigned char> png_encoding; 3842385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch image_diff_png::EncodeRGBAPNG( 38558e6fbe4ee35d65e14b626c557d37565bf8ad179Ben Murdoch diff_image.data(), diff_image.w(), diff_image.h(), 38658e6fbe4ee35d65e14b626c557d37565bf8ad179Ben Murdoch diff_image.w() * 4, &png_encoding); 387a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (base::WriteFile(out_file, 3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) reinterpret_cast<char*>(&png_encoding.front()), 3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::checked_cast<int>(png_encoding.size())) < 0) 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusError; 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusDifferent; 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// It isn't strictly correct to only support ASCII paths, but this 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// program reads paths on stdin and the program that spawns it outputs 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// paths as non-wide strings anyway. 3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath FilePathFromASCII(const std::string& str) { 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 4005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return base::FilePath(base::ASCIIToWide(str)); 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else 4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath(str); 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int main(int argc, const char* argv[]) { 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::EnableTerminationOnHeapCorruption(); 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine::Init(argc, argv); 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 410f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) bool histograms = parsed_command_line.HasSwitch(kOptionCompareHistograms); 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parsed_command_line.HasSwitch(kOptionPollStdin)) { 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Watch stdin for filenames. 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string stdin_buffer; 4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath filename1; 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (std::getline(std::cin, stdin_buffer)) { 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (stdin_buffer.empty()) 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!filename1.empty()) { 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // CompareImages writes results to stdout unless an error occurred. 4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath filename2 = FilePathFromASCII(stdin_buffer); 422f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (CompareImages(filename1, filename2, histograms) == kStatusError) 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) printf("error\n"); 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) fflush(stdout); 4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) filename1 = base::FilePath(); 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Save the first filename in another buffer and wait for the second 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // filename to arrive via stdin. 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filename1 = FilePathFromASCII(stdin_buffer); 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const CommandLine::StringVector& args = parsed_command_line.GetArgs(); 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parsed_command_line.HasSwitch(kOptionGenerateDiff)) { 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (args.size() == 3) { 4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return DiffImages(base::FilePath(args[0]), 4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath(args[1]), 4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath(args[2])); 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (args.size() == 2) { 443f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return CompareImages( 444f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::FilePath(args[0]), base::FilePath(args[1]), histograms); 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PrintHelp(); 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kStatusError; 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 450