// 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/pixel_comparator.h" #include #include #include "base/logging.h" #include "ui/gfx/geometry/rect.h" namespace cc { ExactPixelComparator::ExactPixelComparator(const bool discard_alpha) : discard_alpha_(discard_alpha) { } bool ExactPixelComparator::Compare(const SkBitmap& actual_bmp, const SkBitmap& expected_bmp) const { // Number of pixels with an error int error_pixels_count = 0; gfx::Rect error_bounding_rect = gfx::Rect(); // Check that bitmaps have identical dimensions. DCHECK(actual_bmp.width() == expected_bmp.width() && actual_bmp.height() == expected_bmp.height()); SkAutoLockPixels lock_actual_bmp(actual_bmp); SkAutoLockPixels lock_expected_bmp(expected_bmp); for (int x = 0; x < actual_bmp.width(); ++x) { for (int y = 0; y < actual_bmp.height(); ++y) { SkColor actual_color = actual_bmp.getColor(x, y); SkColor expected_color = expected_bmp.getColor(x, y); if (discard_alpha_) { actual_color = SkColorSetA(actual_color, 0); expected_color = SkColorSetA(expected_color, 0); } if (actual_color != expected_color) { ++error_pixels_count; error_bounding_rect.Union(gfx::Rect(x, y, 1, 1)); } } } if (error_pixels_count != 0) { LOG(ERROR) << "Number of pixel with an error: " << error_pixels_count; LOG(ERROR) << "Error Bounding Box : " << error_bounding_rect.ToString(); return false; } return true; } FuzzyPixelComparator::FuzzyPixelComparator( const bool discard_alpha, const float error_pixels_percentage_limit, const float small_error_pixels_percentage_limit, const float avg_abs_error_limit, const int max_abs_error_limit, const int small_error_threshold) : discard_alpha_(discard_alpha), error_pixels_percentage_limit_(error_pixels_percentage_limit), small_error_pixels_percentage_limit_(small_error_pixels_percentage_limit), avg_abs_error_limit_(avg_abs_error_limit), max_abs_error_limit_(max_abs_error_limit), small_error_threshold_(small_error_threshold) { } bool FuzzyPixelComparator::Compare(const SkBitmap& actual_bmp, const SkBitmap& expected_bmp) const { // Number of pixels with an error int error_pixels_count = 0; // Number of pixels with a small error int small_error_pixels_count = 0; // The per channel sums of absolute errors over all pixels. int64_t sum_abs_error_r = 0; int64_t sum_abs_error_g = 0; int64_t sum_abs_error_b = 0; int64_t sum_abs_error_a = 0; // The per channel maximum absolute errors over all pixels. int max_abs_error_r = 0; int max_abs_error_g = 0; int max_abs_error_b = 0; int max_abs_error_a = 0; gfx::Rect error_bounding_rect = gfx::Rect(); // Check that bitmaps have identical dimensions. DCHECK(actual_bmp.width() == expected_bmp.width() && actual_bmp.height() == expected_bmp.height()); // Check that bitmaps are not empty. DCHECK(actual_bmp.width() > 0 && actual_bmp.height() > 0); SkAutoLockPixels lock_actual_bmp(actual_bmp); SkAutoLockPixels lock_expected_bmp(expected_bmp); for (int x = 0; x < actual_bmp.width(); ++x) { for (int y = 0; y < actual_bmp.height(); ++y) { SkColor actual_color = actual_bmp.getColor(x, y); SkColor expected_color = expected_bmp.getColor(x, y); if (discard_alpha_) { actual_color = SkColorSetA(actual_color, 0); expected_color = SkColorSetA(expected_color, 0); } if (actual_color != expected_color) { ++error_pixels_count; // Compute per channel errors int error_r = SkColorGetR(actual_color) - SkColorGetR(expected_color); int error_g = SkColorGetG(actual_color) - SkColorGetG(expected_color); int error_b = SkColorGetB(actual_color) - SkColorGetB(expected_color); int error_a = SkColorGetA(actual_color) - SkColorGetA(expected_color); int abs_error_r = std::abs(error_r); int abs_error_g = std::abs(error_g); int abs_error_b = std::abs(error_b); int abs_error_a = std::abs(error_a); // Increment small error counter if error is below threshold if (abs_error_r <= small_error_threshold_ && abs_error_g <= small_error_threshold_ && abs_error_b <= small_error_threshold_ && abs_error_a <= small_error_threshold_) ++small_error_pixels_count; // Update per channel maximum absolute errors max_abs_error_r = std::max(max_abs_error_r, abs_error_r); max_abs_error_g = std::max(max_abs_error_g, abs_error_g); max_abs_error_b = std::max(max_abs_error_b, abs_error_b); max_abs_error_a = std::max(max_abs_error_a, abs_error_a); // 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; sum_abs_error_a += abs_error_a; } } } // Compute error metrics from collected data int pixels_count = actual_bmp.width() * actual_bmp.height(); float error_pixels_percentage = 0.0f; float small_error_pixels_percentage = 0.0f; if (pixels_count > 0) { error_pixels_percentage = static_cast(error_pixels_count) / pixels_count * 100.0f; small_error_pixels_percentage = static_cast(small_error_pixels_count) / pixels_count * 100.0f; } float avg_abs_error_r = 0.0f; float avg_abs_error_g = 0.0f; float avg_abs_error_b = 0.0f; float avg_abs_error_a = 0.0f; if (error_pixels_count > 0) { avg_abs_error_r = static_cast(sum_abs_error_r) / error_pixels_count; avg_abs_error_g = static_cast(sum_abs_error_g) / error_pixels_count; avg_abs_error_b = static_cast(sum_abs_error_b) / error_pixels_count; avg_abs_error_a = static_cast(sum_abs_error_a) / error_pixels_count; } if (error_pixels_percentage > error_pixels_percentage_limit_ || small_error_pixels_percentage > small_error_pixels_percentage_limit_ || avg_abs_error_r > avg_abs_error_limit_ || avg_abs_error_g > avg_abs_error_limit_ || avg_abs_error_b > avg_abs_error_limit_ || avg_abs_error_a > avg_abs_error_limit_ || max_abs_error_r > max_abs_error_limit_ || max_abs_error_g > max_abs_error_limit_ || max_abs_error_b > max_abs_error_limit_ || max_abs_error_a > max_abs_error_limit_) { LOG(ERROR) << "Percentage of pixels with an error: " << error_pixels_percentage; LOG(ERROR) << "Percentage of pixels with errors not greater than " << small_error_threshold_ << ": " << small_error_pixels_percentage; LOG(ERROR) << "Average absolute error (excluding identical pixels): " << "R=" << avg_abs_error_r << " " << "G=" << avg_abs_error_g << " " << "B=" << avg_abs_error_b << " " << "A=" << avg_abs_error_a; LOG(ERROR) << "Largest absolute error: " << "R=" << max_abs_error_r << " " << "G=" << max_abs_error_g << " " << "B=" << max_abs_error_b << " " << "A=" << max_abs_error_a; for (int x = 0; x < actual_bmp.width(); ++x) { for (int y = 0; y < actual_bmp.height(); ++y) { SkColor actual_color = actual_bmp.getColor(x, y); SkColor expected_color = expected_bmp.getColor(x, y); if (discard_alpha_) { actual_color = SkColorSetA(actual_color, 0); expected_color = SkColorSetA(expected_color, 0); } if (actual_color != expected_color) error_bounding_rect.Union(gfx::Rect(x, y, 1, 1)); } } LOG(ERROR) << "Error Bounding Box : " << error_bounding_rect.ToString(); return false; } else { return true; } } } // namespace cc