1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/test/pixel_comparator.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "ui/gfx/rect.h"
11
12namespace cc {
13
14ExactPixelComparator::ExactPixelComparator(const bool discard_alpha)
15    : discard_alpha_(discard_alpha) {
16}
17
18bool ExactPixelComparator::Compare(const SkBitmap& actual_bmp,
19                                   const SkBitmap& expected_bmp) const {
20  // Number of pixels with an error
21  int error_pixels_count = 0;
22
23  gfx::Rect error_bounding_rect = gfx::Rect();
24
25  // Check that bitmaps have identical dimensions.
26  DCHECK(actual_bmp.width() == expected_bmp.width() &&
27         actual_bmp.height() == expected_bmp.height());
28
29  SkAutoLockPixels lock_actual_bmp(actual_bmp);
30  SkAutoLockPixels lock_expected_bmp(expected_bmp);
31
32  for (int x = 0; x < actual_bmp.width(); ++x) {
33    for (int y = 0; y < actual_bmp.height(); ++y) {
34      SkColor actual_color = actual_bmp.getColor(x, y);
35      SkColor expected_color = expected_bmp.getColor(x, y);
36      if (discard_alpha_) {
37        actual_color = SkColorSetA(actual_color, 0);
38        expected_color = SkColorSetA(expected_color, 0);
39      }
40      if (actual_color != expected_color) {
41        ++error_pixels_count;
42        error_bounding_rect.Union(gfx::Rect(x, y, 1, 1));
43      }
44    }
45  }
46
47  if (error_pixels_count != 0) {
48    LOG(ERROR) << "Number of pixel with an error: " << error_pixels_count;
49    LOG(ERROR) << "Error Bounding Box : " << error_bounding_rect.ToString();
50    return false;
51  }
52
53  return true;
54}
55
56FuzzyPixelComparator::FuzzyPixelComparator(
57    const bool discard_alpha,
58    const float error_pixels_percentage_limit,
59    const float small_error_pixels_percentage_limit,
60    const float avg_abs_error_limit,
61    const int max_abs_error_limit,
62    const int small_error_threshold)
63    : discard_alpha_(discard_alpha),
64      error_pixels_percentage_limit_(error_pixels_percentage_limit),
65      small_error_pixels_percentage_limit_(small_error_pixels_percentage_limit),
66      avg_abs_error_limit_(avg_abs_error_limit),
67      max_abs_error_limit_(max_abs_error_limit),
68      small_error_threshold_(small_error_threshold) {
69}
70
71bool FuzzyPixelComparator::Compare(const SkBitmap& actual_bmp,
72                                   const SkBitmap& expected_bmp) const {
73  // Number of pixels with an error
74  int error_pixels_count = 0;
75  // Number of pixels with a small error
76  int small_error_pixels_count = 0;
77  // The per channel sums of absolute errors over all pixels.
78  int64 sum_abs_error_r = 0;
79  int64 sum_abs_error_g = 0;
80  int64 sum_abs_error_b = 0;
81  int64 sum_abs_error_a = 0;
82  // The per channel maximum absolute errors over all pixels.
83  int max_abs_error_r = 0;
84  int max_abs_error_g = 0;
85  int max_abs_error_b = 0;
86  int max_abs_error_a = 0;
87
88  gfx::Rect error_bounding_rect = gfx::Rect();
89
90  // Check that bitmaps have identical dimensions.
91  DCHECK(actual_bmp.width() == expected_bmp.width() &&
92         actual_bmp.height() == expected_bmp.height());
93
94  // Check that bitmaps are not empty.
95  DCHECK(actual_bmp.width() > 0 && actual_bmp.height() > 0);
96
97  SkAutoLockPixels lock_actual_bmp(actual_bmp);
98  SkAutoLockPixels lock_expected_bmp(expected_bmp);
99
100  for (int x = 0; x < actual_bmp.width(); ++x) {
101    for (int y = 0; y < actual_bmp.height(); ++y) {
102      SkColor actual_color = actual_bmp.getColor(x, y);
103      SkColor expected_color = expected_bmp.getColor(x, y);
104      if (discard_alpha_) {
105        actual_color = SkColorSetA(actual_color, 0);
106        expected_color = SkColorSetA(expected_color, 0);
107      }
108
109      if (actual_color != expected_color) {
110        ++error_pixels_count;
111
112        // Compute per channel errors
113        int error_r = SkColorGetR(actual_color) - SkColorGetR(expected_color);
114        int error_g = SkColorGetG(actual_color) - SkColorGetG(expected_color);
115        int error_b = SkColorGetB(actual_color) - SkColorGetB(expected_color);
116        int error_a = SkColorGetA(actual_color) - SkColorGetA(expected_color);
117        int abs_error_r = std::abs(error_r);
118        int abs_error_g = std::abs(error_g);
119        int abs_error_b = std::abs(error_b);
120        int abs_error_a = std::abs(error_a);
121
122        // Increment small error counter if error is below threshold
123        if (abs_error_r <= small_error_threshold_ &&
124            abs_error_g <= small_error_threshold_ &&
125            abs_error_b <= small_error_threshold_ &&
126            abs_error_a <= small_error_threshold_)
127          ++small_error_pixels_count;
128
129        // Update per channel maximum absolute errors
130        max_abs_error_r = std::max(max_abs_error_r, abs_error_r);
131        max_abs_error_g = std::max(max_abs_error_g, abs_error_g);
132        max_abs_error_b = std::max(max_abs_error_b, abs_error_b);
133        max_abs_error_a = std::max(max_abs_error_a, abs_error_a);
134
135        // Update per channel absolute error sums
136        sum_abs_error_r += abs_error_r;
137        sum_abs_error_g += abs_error_g;
138        sum_abs_error_b += abs_error_b;
139        sum_abs_error_a += abs_error_a;
140      }
141    }
142  }
143
144  // Compute error metrics from collected data
145  int pixels_count = actual_bmp.width() * actual_bmp.height();
146  float error_pixels_percentage = 0.0f;
147  float small_error_pixels_percentage = 0.0f;
148  if (pixels_count > 0) {
149    error_pixels_percentage = static_cast<float>(error_pixels_count) /
150        pixels_count * 100.0f;
151    small_error_pixels_percentage =
152        static_cast<float>(small_error_pixels_count) / pixels_count * 100.0f;
153  }
154  float avg_abs_error_r = 0.0f;
155  float avg_abs_error_g = 0.0f;
156  float avg_abs_error_b = 0.0f;
157  float avg_abs_error_a = 0.0f;
158  if (error_pixels_count > 0) {
159    avg_abs_error_r = static_cast<float>(sum_abs_error_r) / error_pixels_count;
160    avg_abs_error_g = static_cast<float>(sum_abs_error_g) / error_pixels_count;
161    avg_abs_error_b = static_cast<float>(sum_abs_error_b) / error_pixels_count;
162    avg_abs_error_a = static_cast<float>(sum_abs_error_a) / error_pixels_count;
163  }
164
165  if (error_pixels_percentage > error_pixels_percentage_limit_ ||
166      small_error_pixels_percentage > small_error_pixels_percentage_limit_ ||
167      avg_abs_error_r > avg_abs_error_limit_ ||
168      avg_abs_error_g > avg_abs_error_limit_ ||
169      avg_abs_error_b > avg_abs_error_limit_ ||
170      avg_abs_error_a > avg_abs_error_limit_ ||
171      max_abs_error_r > max_abs_error_limit_ ||
172      max_abs_error_g > max_abs_error_limit_ ||
173      max_abs_error_b > max_abs_error_limit_ ||
174      max_abs_error_a > max_abs_error_limit_) {
175    LOG(ERROR) << "Percentage of pixels with an error: "
176               << error_pixels_percentage;
177    LOG(ERROR) << "Percentage of pixels with errors not greater than "
178               << small_error_threshold_ << ": "
179               << small_error_pixels_percentage;
180    LOG(ERROR) << "Average absolute error (excluding identical pixels): "
181               << "R=" << avg_abs_error_r << " "
182               << "G=" << avg_abs_error_g << " "
183               << "B=" << avg_abs_error_b << " "
184               << "A=" << avg_abs_error_a;
185    LOG(ERROR) << "Largest absolute error: "
186               << "R=" << max_abs_error_r << " "
187               << "G=" << max_abs_error_g << " "
188               << "B=" << max_abs_error_b << " "
189               << "A=" << max_abs_error_a;
190
191      for (int x = 0; x < actual_bmp.width(); ++x) {
192        for (int y = 0; y < actual_bmp.height(); ++y) {
193          SkColor actual_color = actual_bmp.getColor(x, y);
194          SkColor expected_color = expected_bmp.getColor(x, y);
195          if (discard_alpha_) {
196            actual_color = SkColorSetA(actual_color, 0);
197            expected_color = SkColorSetA(expected_color, 0);
198          }
199          if (actual_color != expected_color)
200            error_bounding_rect.Union(gfx::Rect(x, y, 1, 1));
201        }
202      }
203      LOG(ERROR) << "Error Bounding Box : " << error_bounding_rect.ToString();
204    return false;
205  } else {
206    return true;
207  }
208}
209
210}  // namespace cc
211