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