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 <stdint.h>
8 
9 #include <algorithm>
10 
11 #include "base/check_op.h"
12 #include "base/logging.h"
13 #include "ui/gfx/geometry/rect.h"
14 
15 namespace cc {
16 
ExactPixelComparator(const bool discard_alpha)17 ExactPixelComparator::ExactPixelComparator(const bool discard_alpha)
18     : discard_alpha_(discard_alpha) {
19 }
20 
Compare(const SkBitmap & actual_bmp,const SkBitmap & expected_bmp) const21 bool ExactPixelComparator::Compare(const SkBitmap& actual_bmp,
22                                    const SkBitmap& expected_bmp) const {
23   // Number of pixels with an error
24   int error_pixels_count = 0;
25 
26   gfx::Rect error_bounding_rect = gfx::Rect();
27 
28   // Check that bitmaps have identical dimensions.
29   DCHECK_EQ(actual_bmp.width(), expected_bmp.width());
30   DCHECK_EQ(actual_bmp.height(), expected_bmp.height());
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 
ManhattanDistancePixelComparator(int tolerance)56 ManhattanDistancePixelComparator::ManhattanDistancePixelComparator(
57     int tolerance)
58     : tolerance_(tolerance) {}
59 
Compare(const SkBitmap & actual_bmp,const SkBitmap & expected_bmp) const60 bool ManhattanDistancePixelComparator::Compare(
61     const SkBitmap& actual_bmp,
62     const SkBitmap& expected_bmp) const {
63   // Check that bitmaps have identical dimensions.
64   DCHECK_EQ(actual_bmp.width(), expected_bmp.width());
65   DCHECK_EQ(actual_bmp.height(), expected_bmp.height());
66 
67   for (int y = 0; y < actual_bmp.height(); ++y) {
68     for (int x = 0; x < actual_bmp.width(); ++x) {
69       SkColor actual_color = actual_bmp.getColor(x, y);
70       SkColor expected_color = expected_bmp.getColor(x, y);
71 
72       int pixel_b = SkColorGetB(actual_color);
73       int pixel_g = SkColorGetG(actual_color);
74       int pixel_r = SkColorGetR(actual_color);
75 
76       int ref_pixel_b = SkColorGetB(expected_color);
77       int ref_pixel_g = SkColorGetG(expected_color);
78       int ref_pixel_r = SkColorGetR(expected_color);
79 
80       int manhattan_distance = std::abs(pixel_b - ref_pixel_b) +
81                                std::abs(pixel_g - ref_pixel_g) +
82                                std::abs(pixel_r - ref_pixel_r);
83 
84       if (manhattan_distance > tolerance_) {
85         LOG(ERROR) << "Pixel test failed on (" << x << ", " << y << "). "
86                    << "Manhattan distance: " << manhattan_distance << ".";
87         return false;
88       }
89     }
90   }
91 
92   return true;
93 }
94 
FuzzyPixelComparator(bool discard_alpha,float error_pixels_percentage_limit,float small_error_pixels_percentage_limit,float avg_abs_error_limit,int max_abs_error_limit,int small_error_threshold,bool check_critical_error)95 FuzzyPixelComparator::FuzzyPixelComparator(
96     bool discard_alpha,
97     float error_pixels_percentage_limit,
98     float small_error_pixels_percentage_limit,
99     float avg_abs_error_limit,
100     int max_abs_error_limit,
101     int small_error_threshold,
102     bool check_critical_error)
103     : discard_alpha_(discard_alpha),
104       error_pixels_percentage_limit_(error_pixels_percentage_limit),
105       small_error_pixels_percentage_limit_(small_error_pixels_percentage_limit),
106       avg_abs_error_limit_(avg_abs_error_limit),
107       max_abs_error_limit_(max_abs_error_limit),
108       small_error_threshold_(small_error_threshold),
109       check_critical_error_(check_critical_error) {}
110 
Compare(const SkBitmap & actual_bmp,const SkBitmap & expected_bmp) const111 bool FuzzyPixelComparator::Compare(const SkBitmap& actual_bmp,
112                                    const SkBitmap& expected_bmp) const {
113   // Number of pixels with an error
114   int error_pixels_count = 0;
115   // Number of pixels with a small error
116   int small_error_pixels_count = 0;
117   // Number of pixels with a critical error.
118   int critial_error_pixels_count = 0;
119   // The per channel sums of absolute errors over all pixels.
120   int64_t sum_abs_error_r = 0;
121   int64_t sum_abs_error_g = 0;
122   int64_t sum_abs_error_b = 0;
123   int64_t sum_abs_error_a = 0;
124   // The per channel maximum absolute errors over all pixels.
125   int max_abs_error_r = 0;
126   int max_abs_error_g = 0;
127   int max_abs_error_b = 0;
128   int max_abs_error_a = 0;
129 
130   gfx::Rect error_bounding_rect = gfx::Rect();
131 
132   // Check that bitmaps have identical dimensions.
133   DCHECK_EQ(actual_bmp.width(), expected_bmp.width());
134   DCHECK_EQ(actual_bmp.height(), expected_bmp.height());
135 
136   // Check that bitmaps are not empty.
137   DCHECK_GT(actual_bmp.width(), 0);
138   DCHECK_GT(actual_bmp.height(), 0);
139 
140   for (int x = 0; x < actual_bmp.width(); ++x) {
141     for (int y = 0; y < actual_bmp.height(); ++y) {
142       SkColor actual_color = actual_bmp.getColor(x, y);
143       SkColor expected_color = expected_bmp.getColor(x, y);
144       if (discard_alpha_) {
145         actual_color = SkColorSetA(actual_color, 0);
146         expected_color = SkColorSetA(expected_color, 0);
147       }
148 
149       if (actual_color != expected_color) {
150         ++error_pixels_count;
151 
152         // Compute per channel errors
153         uint32_t expected_alpha = SkColorGetA(expected_color);
154         int error_r = SkColorGetR(actual_color) - SkColorGetR(expected_color);
155         int error_g = SkColorGetG(actual_color) - SkColorGetG(expected_color);
156         int error_b = SkColorGetB(actual_color) - SkColorGetB(expected_color);
157         int error_a = SkColorGetA(actual_color) - expected_alpha;
158         int abs_error_r = std::abs(error_r);
159         int abs_error_g = std::abs(error_g);
160         int abs_error_b = std::abs(error_b);
161         int abs_error_a = std::abs(error_a);
162 
163         // Increment small error counter if error is below threshold
164         if (abs_error_r <= small_error_threshold_ &&
165             abs_error_g <= small_error_threshold_ &&
166             abs_error_b <= small_error_threshold_ &&
167             abs_error_a <= small_error_threshold_)
168           ++small_error_pixels_count;
169 
170         if (check_critical_error_ && abs_error_a != 0 &&
171             (expected_alpha == 0 || expected_alpha == 0xff))
172           ++critial_error_pixels_count;
173 
174         // Update per channel maximum absolute errors
175         max_abs_error_r = std::max(max_abs_error_r, abs_error_r);
176         max_abs_error_g = std::max(max_abs_error_g, abs_error_g);
177         max_abs_error_b = std::max(max_abs_error_b, abs_error_b);
178         max_abs_error_a = std::max(max_abs_error_a, abs_error_a);
179 
180         // Update per channel absolute error sums
181         sum_abs_error_r += abs_error_r;
182         sum_abs_error_g += abs_error_g;
183         sum_abs_error_b += abs_error_b;
184         sum_abs_error_a += abs_error_a;
185       }
186     }
187   }
188 
189   // Compute error metrics from collected data
190   int pixels_count = actual_bmp.width() * actual_bmp.height();
191   float error_pixels_percentage = 0.0f;
192   float small_error_pixels_percentage = 0.0f;
193   if (pixels_count > 0) {
194     error_pixels_percentage = static_cast<float>(error_pixels_count) /
195         pixels_count * 100.0f;
196     small_error_pixels_percentage =
197         static_cast<float>(small_error_pixels_count) / pixels_count * 100.0f;
198   }
199   float avg_abs_error_r = 0.0f;
200   float avg_abs_error_g = 0.0f;
201   float avg_abs_error_b = 0.0f;
202   float avg_abs_error_a = 0.0f;
203   if (error_pixels_count > 0) {
204     avg_abs_error_r = static_cast<float>(sum_abs_error_r) / error_pixels_count;
205     avg_abs_error_g = static_cast<float>(sum_abs_error_g) / error_pixels_count;
206     avg_abs_error_b = static_cast<float>(sum_abs_error_b) / error_pixels_count;
207     avg_abs_error_a = static_cast<float>(sum_abs_error_a) / error_pixels_count;
208   }
209 
210   if (error_pixels_percentage > error_pixels_percentage_limit_ ||
211       small_error_pixels_percentage > small_error_pixels_percentage_limit_ ||
212       avg_abs_error_r > avg_abs_error_limit_ ||
213       avg_abs_error_g > avg_abs_error_limit_ ||
214       avg_abs_error_b > avg_abs_error_limit_ ||
215       avg_abs_error_a > avg_abs_error_limit_ ||
216       max_abs_error_r > max_abs_error_limit_ ||
217       max_abs_error_g > max_abs_error_limit_ ||
218       max_abs_error_b > max_abs_error_limit_ ||
219       max_abs_error_a > max_abs_error_limit_ || critial_error_pixels_count) {
220     LOG(ERROR) << "Percentage of pixels with an error: "
221                << error_pixels_percentage;
222     LOG(ERROR) << "Percentage of pixels with errors not greater than "
223                << small_error_threshold_ << ": "
224                << small_error_pixels_percentage;
225     LOG(ERROR) << "Average absolute error (excluding identical pixels): "
226                << "R=" << avg_abs_error_r << " "
227                << "G=" << avg_abs_error_g << " "
228                << "B=" << avg_abs_error_b << " "
229                << "A=" << avg_abs_error_a;
230     LOG(ERROR) << "Largest absolute error: "
231                << "R=" << max_abs_error_r << " "
232                << "G=" << max_abs_error_g << " "
233                << "B=" << max_abs_error_b << " "
234                << "A=" << max_abs_error_a;
235     LOG(ERROR) << "Critical error: " << critial_error_pixels_count;
236 
237     for (int x = 0; x < actual_bmp.width(); ++x) {
238       for (int y = 0; y < actual_bmp.height(); ++y) {
239         SkColor actual_color = actual_bmp.getColor(x, y);
240         SkColor expected_color = expected_bmp.getColor(x, y);
241         if (discard_alpha_) {
242           actual_color = SkColorSetA(actual_color, 0);
243           expected_color = SkColorSetA(expected_color, 0);
244         }
245         if (actual_color != expected_color)
246           error_bounding_rect.Union(gfx::Rect(x, y, 1, 1));
247       }
248     }
249     LOG(ERROR) << "Error Bounding Box : " << error_bounding_rect.ToString();
250     return false;
251   } else {
252     return true;
253   }
254 }
255 
256 }  // namespace cc
257