Index: cc/test/image_comparator.cc |
diff --git a/cc/test/image_comparator.cc b/cc/test/image_comparator.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..844a22a53ea03ff2a10a98b43d8378b7438e8a98 |
--- /dev/null |
+++ b/cc/test/image_comparator.cc |
@@ -0,0 +1,179 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "cc/test/image_comparator.h" |
+ |
+#include <algorithm> |
+ |
+namespace cc { |
+ |
+ImageComparator::ImageComparator() { |
+ thresholds_ = ErrorMetrics(); |
+} |
+ |
+ImageComparator::ImageComparator(const ErrorMetrics& thresholds) |
+ : thresholds_(thresholds) |
+{ } |
+ |
+ImageComparator::ErrorMetrics::ErrorMetrics(float error_pixels_percentage, |
+ float small_error_pixels_percentage, |
+ float avg_abs_error_r, |
+ float avg_abs_error_g, |
+ float avg_abs_error_b, |
+ unsigned int max_abs_error_r, |
+ unsigned int max_abs_error_g, |
+ unsigned int max_abs_error_b, |
+ unsigned int small_error_threshold) |
+ : error_pixels_percentage(error_pixels_percentage), |
+ small_error_pixels_percentage(small_error_pixels_percentage), |
+ avg_abs_error_r(avg_abs_error_r), |
+ avg_abs_error_g(avg_abs_error_g), |
+ avg_abs_error_b(avg_abs_error_b), |
+ max_abs_error_r(max_abs_error_r), |
+ max_abs_error_g(max_abs_error_g), |
+ max_abs_error_b(max_abs_error_b), |
+ small_error_threshold(small_error_threshold) |
+{ } |
+ |
+void ImageComparator::AllowOffByOneErrors(float off_by_one_pixels_percentage) { |
+ thresholds_.error_pixels_percentage = off_by_one_pixels_percentage; |
+ thresholds_.small_error_pixels_percentage = off_by_one_pixels_percentage; |
+ thresholds_.avg_abs_error_r = 1.0f; |
+ thresholds_.avg_abs_error_g = 1.0f; |
+ thresholds_.avg_abs_error_b = 1.0f; |
+ thresholds_.max_abs_error_r = 1.0f; |
+ thresholds_.max_abs_error_g = 1.0f; |
+ thresholds_.max_abs_error_b = 1.0f; |
+ thresholds_.small_error_threshold = 1; |
+} |
+ |
+bool ImageComparator::Compare(const SkBitmap& bitmap_a, |
+ const SkBitmap& bitmap_b, |
+ ErrorMetrics* return_metrics) const { |
+ ErrorMetrics metrics; |
+ metrics.small_error_threshold = thresholds_.small_error_threshold; |
+ |
+ // Number of pixels that are different. |
+ unsigned int error_pixels_count = 0; |
+ // Number of pixels that are at most off by small_error_threshold per color |
+ // channel |
+ unsigned int small_error_pixels_count = 0; |
+ // The per channel sum of absolute errors over all pixels. |
+ unsigned long long sum_abs_error_r = 0; |
+ unsigned long long sum_abs_error_g = 0; |
+ unsigned long long sum_abs_error_b = 0; |
+ |
+ // A meaningful comparison is not possible if the size does not match |
+ // (alignment? background color?). Return maximum error values in that case. |
+ if (bitmap_a.width() != bitmap_b.width() || |
+ bitmap_a.height() != bitmap_b.height()) { |
reveman
2013/03/07 07:50:44
MatchesPNGFile already check the size. DCHECK is e
ernstm
2013/03/07 19:30:07
Moved all dimension checking into Compare function
|
+ if (return_metrics != NULL) { |
+ return_metrics->error_pixels_percentage = 100.0f; |
+ return_metrics->small_error_pixels_percentage = 0.0f; |
+ return_metrics->avg_abs_error_r = 255.0f; |
+ return_metrics->avg_abs_error_g = 255.0f; |
+ return_metrics->avg_abs_error_b = 255.0f; |
+ return_metrics->max_abs_error_r = 255; |
+ return_metrics->max_abs_error_g = 255; |
+ return_metrics->max_abs_error_b = 255; |
+ return_metrics->small_error_threshold = thresholds_.small_error_threshold; |
+ } |
+ return false; |
+ } |
+ |
+ // Empty bitmaps are always considered to be equal. |
+ if (bitmap_a.width() == 0 || bitmap_a.height() == 0) { |
reveman
2013/03/07 07:50:44
move this to MatchesPNGFile if needed. DCHECK at m
ernstm
2013/03/07 19:30:07
This got much simpler in the refactored code.
On 2
|
+ if (return_metrics != NULL) { |
+ return_metrics->error_pixels_percentage = 0.0f; |
+ return_metrics->small_error_pixels_percentage = 0.0f; |
+ return_metrics->avg_abs_error_r = 0.0f; |
+ return_metrics->avg_abs_error_g = 0.0f; |
+ return_metrics->avg_abs_error_b = 0.0f; |
+ return_metrics->max_abs_error_r = 0; |
+ return_metrics->max_abs_error_g = 0; |
+ return_metrics->max_abs_error_b = 0; |
+ return_metrics->small_error_threshold = thresholds_.small_error_threshold; |
+ } |
+ return true; |
+ } |
+ |
+ SkAutoLockPixels lock_bitmap_a(bitmap_a); |
+ SkAutoLockPixels lock_bitmap_b(bitmap_b); |
+ |
+ // The reference images were saved with no alpha channel. Use the mask to |
+ // set alpha to 0. |
+ uint32_t kAlphaMask = 0x00FFFFFF; |
+ for (int x = 0; x < bitmap_a.width(); ++x) { |
+ for (int y = 0; y < bitmap_a.height(); ++y) { |
+ if ((*bitmap_a.getAddr32(x, y) & kAlphaMask) != |
+ (*bitmap_b.getAddr32(x, y) & kAlphaMask)) { |
+ ++error_pixels_count; |
+ |
+ SkColor color_a = bitmap_a.getColor(x, y); |
+ SkColor color_b = bitmap_b.getColor(x, y); |
+ |
+ // Compute per channel absolute errors |
+ unsigned int abs_error_r = |
+ std::max(SkColorGetR(color_a), SkColorGetR(color_b)) - |
+ std::min(SkColorGetR(color_a), SkColorGetR(color_b)); |
+ unsigned int abs_error_g = |
+ std::max(SkColorGetG(color_a), SkColorGetG(color_b)) - |
+ std::min(SkColorGetG(color_a), SkColorGetG(color_b)); |
+ unsigned int abs_error_b = |
+ std::max(SkColorGetB(color_a), SkColorGetB(color_b)) - |
+ std::min(SkColorGetB(color_a), SkColorGetB(color_b)); |
+ |
+ // Increment small error counter if error is below threshold |
+ if (abs_error_r <= thresholds_.small_error_threshold && |
+ abs_error_g <= thresholds_.small_error_threshold && |
+ abs_error_b <= thresholds_.small_error_threshold) |
+ ++small_error_pixels_count; |
+ |
+ // Update per channel maximum absolute errors |
+ if (abs_error_r > metrics.max_abs_error_r) |
+ metrics.max_abs_error_r = abs_error_r; |
+ if (abs_error_g > metrics.max_abs_error_g) |
+ metrics.max_abs_error_g = abs_error_g; |
+ if (abs_error_b > metrics.max_abs_error_b) |
+ metrics.max_abs_error_b = abs_error_b; |
+ |
+ // Update per channel absolute error sums |
+ sum_abs_error_r += abs_error_r; |
+ sum_abs_error_g += abs_error_g; |
+ sum_abs_error_b += abs_error_b; |
+ } |
+ } |
+ } |
+ |
+ // Fill error metrics with collected data |
+ unsigned int pixels_count = bitmap_a.width() * bitmap_a.height(); |
+ metrics.error_pixels_percentage = static_cast<float>(error_pixels_count) / |
+ pixels_count * 100.0f; |
+ metrics.small_error_pixels_percentage = static_cast<float>(small_error_pixels_count) / |
+ pixels_count * 100.0f; |
+ if (error_pixels_count > 0) { |
+ metrics.avg_abs_error_r = static_cast<float>(sum_abs_error_r) / error_pixels_count; |
+ metrics.avg_abs_error_g = static_cast<float>(sum_abs_error_g) / error_pixels_count; |
+ metrics.avg_abs_error_b = static_cast<float>(sum_abs_error_b) / error_pixels_count; |
+ } else { |
+ metrics.avg_abs_error_r = 0; |
+ metrics.avg_abs_error_g = 0; |
+ metrics.avg_abs_error_b = 0; |
+ } |
+ |
+ // Return error_ etrics using passed in pointer if not NULL |
+ if (return_metrics != NULL) *return_metrics = metrics; |
+ |
+ // Return true if all collected error metrics are below the comparator's thresholds. |
+ return (metrics.error_pixels_percentage <= thresholds_.error_pixels_percentage && |
+ metrics.small_error_pixels_percentage <= thresholds_.small_error_pixels_percentage && |
+ metrics.avg_abs_error_r <= thresholds_.avg_abs_error_r && |
+ metrics.avg_abs_error_g <= thresholds_.avg_abs_error_g && |
+ metrics.avg_abs_error_b <= thresholds_.avg_abs_error_b && |
+ metrics.max_abs_error_r <= thresholds_.max_abs_error_r && |
+ metrics.max_abs_error_g <= thresholds_.max_abs_error_g && |
+ metrics.max_abs_error_b <= thresholds_.max_abs_error_b); |
+} |
+ |
+} // namespace cc |