Chromium Code Reviews| 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 |