// 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 <algorithm>
#include "base/logging.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;
// 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;
LOG(ERROR) << "Pixel error at x=" << x << " y=" << y << "; "
<< "actual RGBA=("
<< SkColorGetR(actual_color) << ","
<< SkColorGetG(actual_color) << ","
<< SkColorGetB(actual_color) << ","
<< SkColorGetA(actual_color) << "); "
<< "expected RGBA=("
<< SkColorGetR(expected_color) << ","
<< SkColorGetG(expected_color) << ","
<< SkColorGetB(expected_color) << ","
<< SkColorGetA(expected_color) << ")";
}
}
}
if (error_pixels_count != 0) {
LOG(ERROR) << "Number of pixel with an error: " << error_pixels_count;
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 sum_abs_error_r = 0;
int64 sum_abs_error_g = 0;
int64 sum_abs_error_b = 0;
int64 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;
// 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<float>(error_pixels_count) /
pixels_count * 100.0f;
small_error_pixels_percentage =
static_cast<float>(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<float>(sum_abs_error_r) / error_pixels_count;
avg_abs_error_g = static_cast<float>(sum_abs_error_g) / error_pixels_count;
avg_abs_error_b = static_cast<float>(sum_abs_error_b) / error_pixels_count;
avg_abs_error_a = static_cast<float>(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) {
LOG(ERROR) << "Pixel error at x=" << x << " y=" << y << "; "
<< "actual RGBA=("
<< SkColorGetR(actual_color) << ","
<< SkColorGetG(actual_color) << ","
<< SkColorGetB(actual_color) << ","
<< SkColorGetA(actual_color) << "); "
<< "expected RGBA=("
<< SkColorGetR(expected_color) << ","
<< SkColorGetG(expected_color) << ","
<< SkColorGetB(expected_color) << ","
<< SkColorGetA(expected_color) << ")";
}
}
}
return false;
} else {
return true;
}
}
} // namespace cc