This source file includes following definitions.
- current_result_index_
- AddSample
- GetSample
- ChannelApproximatelyEqual
- Calculate8bitBitmapMinMax
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
#include "ui/gfx/color_analysis.h"
#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/rect.h"
using color_utils::FindClosestColor;
namespace {
const unsigned char k1x1White[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,
0xde, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47,
0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00,
0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00,
0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74,
0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x11, 0x15,
0x16, 0x1b, 0xaa, 0x58, 0x38, 0x76, 0x00, 0x00,
0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f,
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69,
0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57,
0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x49,
0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff,
0xff, 0x3f, 0x00, 0x05, 0xfe, 0x02, 0xfe, 0xdc,
0xcc, 0x59, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x49,
0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
const unsigned char k1x3BlueWhite[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
0x08, 0x02, 0x00, 0x00, 0x00, 0xdd, 0xbf, 0xf2,
0xd5, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47,
0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00,
0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00,
0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74,
0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x12, 0x01,
0x0a, 0x2c, 0xfd, 0x08, 0x64, 0x66, 0x00, 0x00,
0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f,
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69,
0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57,
0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x14, 0x49,
0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff,
0xff, 0x3f, 0x13, 0x03, 0x03, 0x03, 0x03, 0x03,
0xc3, 0x7f, 0x00, 0x1e, 0xfd, 0x03, 0xff, 0xde,
0x72, 0x58, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x49,
0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
const unsigned char k1x3BlueRed[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
0x08, 0x02, 0x00, 0x00, 0x00, 0xdd, 0xbf, 0xf2,
0xd5, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47,
0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00,
0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00,
0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74,
0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x12, 0x01,
0x07, 0x09, 0x03, 0xa2, 0xce, 0x6c, 0x00, 0x00,
0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f,
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69,
0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57,
0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x14, 0x49,
0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xcf,
0xc0, 0xc0, 0xc4, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
0xf0, 0x1f, 0x00, 0x0c, 0x10, 0x02, 0x01, 0x2c,
0x8f, 0x8b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x49,
0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
class MockKMeanImageSampler : public color_utils::KMeanImageSampler {
public:
MockKMeanImageSampler() : current_result_index_(0) {
}
explicit MockKMeanImageSampler(const std::vector<int>& samples)
: prebaked_sample_results_(samples),
current_result_index_(0) {
}
virtual ~MockKMeanImageSampler() {
}
void AddSample(int sample) {
prebaked_sample_results_.push_back(sample);
}
virtual int GetSample(int width, int height) OVERRIDE {
if (current_result_index_ >= prebaked_sample_results_.size()) {
current_result_index_ = 0;
}
if (prebaked_sample_results_.empty()) {
return 0;
}
return prebaked_sample_results_[current_result_index_++];
}
protected:
std::vector<int> prebaked_sample_results_;
size_t current_result_index_;
};
bool ChannelApproximatelyEqual(int expected, uint8_t channel) {
return (abs(expected - static_cast<int>(channel)) <= 1);
}
void Calculate8bitBitmapMinMax(const SkBitmap& bitmap,
uint8_t* min_gl,
uint8_t* max_gl) {
SkAutoLockPixels bitmap_lock(bitmap);
DCHECK(bitmap.getPixels());
DCHECK(bitmap.config() == SkBitmap::kA8_Config);
DCHECK(min_gl);
DCHECK(max_gl);
*min_gl = std::numeric_limits<uint8_t>::max();
*max_gl = std::numeric_limits<uint8_t>::min();
for (int y = 0; y < bitmap.height(); ++y) {
uint8_t* current_color = bitmap.getAddr8(0, y);
for (int x = 0; x < bitmap.width(); ++x, ++current_color) {
*min_gl = std::min(*min_gl, *current_color);
*max_gl = std::max(*max_gl, *current_color);
}
}
}
}
class ColorAnalysisTest : public testing::Test {
};
TEST_F(ColorAnalysisTest, CalculatePNGKMeanAllWhite) {
MockKMeanImageSampler test_sampler;
test_sampler.AddSample(0);
scoped_refptr<base::RefCountedBytes> png(
new base::RefCountedBytes(
std::vector<unsigned char>(
k1x1White,
k1x1White + sizeof(k1x1White) / sizeof(unsigned char))));
SkColor color =
color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler);
EXPECT_EQ(color, SK_ColorWHITE);
}
TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhite) {
MockKMeanImageSampler test_sampler;
test_sampler.AddSample(0);
test_sampler.AddSample(1);
test_sampler.AddSample(2);
scoped_refptr<base::RefCountedBytes> png(
new base::RefCountedBytes(
std::vector<unsigned char>(
k1x3BlueWhite,
k1x3BlueWhite + sizeof(k1x3BlueWhite) / sizeof(unsigned char))));
SkColor color =
color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler);
EXPECT_EQ(color, SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
}
TEST_F(ColorAnalysisTest, CalculatePNGKMeanPickMostCommon) {
MockKMeanImageSampler test_sampler;
test_sampler.AddSample(0);
test_sampler.AddSample(1);
test_sampler.AddSample(2);
scoped_refptr<base::RefCountedBytes> png(
new base::RefCountedBytes(
std::vector<unsigned char>(
k1x3BlueRed,
k1x3BlueRed + sizeof(k1x3BlueRed) / sizeof(unsigned char))));
SkColor color =
color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler);
EXPECT_EQ(color, SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
}
TEST_F(ColorAnalysisTest, GridSampler) {
color_utils::GridSampler sampler;
const int kWidth = 16;
const int kHeight = 16;
EXPECT_EQ(1 + 1 * kWidth, sampler.GetSample(kWidth, kHeight));
EXPECT_EQ(1 + 4 * kWidth, sampler.GetSample(kWidth, kHeight));
EXPECT_EQ(1 + 7 * kWidth, sampler.GetSample(kWidth, kHeight));
EXPECT_EQ(1 + 10 * kWidth, sampler.GetSample(kWidth, kHeight));
EXPECT_EQ(4 + 1 * kWidth, sampler.GetSample(kWidth, kHeight));
EXPECT_EQ(4 + 4 * kWidth, sampler.GetSample(kWidth, kHeight));
EXPECT_EQ(4 + 7 * kWidth, sampler.GetSample(kWidth, kHeight));
EXPECT_EQ(4 + 10 * kWidth, sampler.GetSample(kWidth, kHeight));
}
TEST_F(ColorAnalysisTest, FindClosestColor) {
SkColor color = FindClosestColor(NULL, 0, 0, SK_ColorRED);
EXPECT_EQ(SK_ColorRED, color);
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
bitmap.allocPixels();
bitmap.eraseColor(SK_ColorWHITE);
color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()),
bitmap.width(),
bitmap.height(),
SK_ColorRED);
EXPECT_EQ(SK_ColorWHITE, color);
uint32_t* pixel = bitmap.getAddr32(0, 0);
*pixel = SK_ColorBLACK;
color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()),
bitmap.width(),
bitmap.height(),
SK_ColorDKGRAY);
EXPECT_EQ(SK_ColorBLACK, color);
}
TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) {
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
bitmap.allocPixels();
bitmap.eraseARGB(255, 100, 150, 200);
SkColor color = color_utils::CalculateKMeanColorOfBitmap(bitmap);
EXPECT_EQ(255u, SkColorGetA(color));
EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
bitmap.eraseARGB(128, 100, 150, 200);
color = color_utils::CalculateKMeanColorOfBitmap(bitmap);
EXPECT_EQ(255u, SkColorGetA(color));
EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
}
TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) {
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 200);
EXPECT_EQ(gfx::Matrix3F::Zeros(),
color_utils::ComputeColorCovariance(bitmap));
bitmap.allocPixels();
bitmap.eraseRGB(50, 150, 200);
gfx::Matrix3F covariance = color_utils::ComputeColorCovariance(bitmap);
EXPECT_TRUE(covariance == gfx::Matrix3F::Zeros());
}
TEST_F(ColorAnalysisTest, ComputeColorCovarianceWithCanvas) {
gfx::Canvas canvas(gfx::Size(250, 200), 1.0f, true);
canvas.FillRect(gfx::Rect(0, 0, 50, 200), SkColorSetRGB(100, 0, 0));
canvas.FillRect(gfx::Rect(50, 0, 50, 200), SkColorSetRGB(100, 100, 0));
canvas.FillRect(gfx::Rect(100, 0, 50, 200), SkColorSetRGB(100, 100, 100));
canvas.FillRect(gfx::Rect(150, 0, 50, 200), SkColorSetRGB(0, 100, 100));
canvas.FillRect(gfx::Rect(200, 0, 50, 200), SkColorSetRGB(0, 0, 100));
SkBitmap bitmap =
skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
gfx::Matrix3F covariance = color_utils::ComputeColorCovariance(bitmap);
gfx::Matrix3F expected_covariance = gfx::Matrix3F::Zeros();
expected_covariance.set(2400, 400, -1600,
400, 2400, 400,
-1600, 400, 2400);
EXPECT_EQ(expected_covariance, covariance);
}
TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) {
SkBitmap source, result;
source.setConfig(SkBitmap::kARGB_8888_Config, 300, 200);
result.setConfig(SkBitmap::kA8_Config, 300, 200);
source.allocPixels();
result.allocPixels();
source.eraseRGB(50, 150, 200);
gfx::Vector3dF transform(1.0f, .5f, 0.1f);
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, false, &result));
uint8_t min_gl = 0;
uint8_t max_gl = 0;
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(145, min_gl);
EXPECT_EQ(145, max_gl);
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, true, &result));
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(0, min_gl);
EXPECT_EQ(0, max_gl);
transform.set_z(1.1f);
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, false, &result));
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(0xFF, min_gl);
EXPECT_EQ(0xFF, max_gl);
transform.Scale(-1.0f);
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, false, &result));
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(0x0, min_gl);
EXPECT_EQ(0x0, max_gl);
}
TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) {
gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
canvas.FillRect(gfx::Rect(0, 0, 150, 200), SkColorSetRGB(0, 0, 0));
canvas.FillRect(gfx::Rect(150, 0, 150, 200), SkColorSetRGB(255, 255, 255));
SkBitmap source =
skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
SkBitmap result;
result.setConfig(SkBitmap::kA8_Config, 300, 200);
result.allocPixels();
gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, true, &result));
uint8_t min_gl = 0;
uint8_t max_gl = 0;
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(0, min_gl);
EXPECT_EQ(255, max_gl);
EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0)));
EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199)));
transform.Scale(-1.0f);
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, true, &result));
min_gl = 0;
max_gl = 0;
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(0, min_gl);
EXPECT_EQ(255, max_gl);
EXPECT_EQ(max_gl, SkColorGetA(result.getColor(0, 0)));
EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
}
TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) {
gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(100, 0, 0));
canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(0, 255, 0));
canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(0, 0, 128));
SkBitmap source =
skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
SkBitmap result;
result.setConfig(SkBitmap::kA8_Config, 300, 200);
result.allocPixels();
gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, false, &result));
uint8_t min_gl = 0;
uint8_t max_gl = 0;
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(12, min_gl);
EXPECT_EQ(127, max_gl);
EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
EXPECT_EQ(100U, SkColorGetA(result.getColor(0, 0)));
EXPECT_TRUE(color_utils::ApplyColorReduction(
source, transform, true, &result));
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(0, min_gl);
EXPECT_EQ(255, max_gl);
EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
EXPECT_EQ(193U, SkColorGetA(result.getColor(0, 0)));
}
TEST_F(ColorAnalysisTest, ComputePrincipalComponentImageNotComputable) {
SkBitmap source, result;
source.setConfig(SkBitmap::kARGB_8888_Config, 300, 200);
result.setConfig(SkBitmap::kA8_Config, 300, 200);
source.allocPixels();
result.allocPixels();
source.eraseRGB(50, 150, 200);
EXPECT_FALSE(color_utils::ComputePrincipalComponentImage(source, &result));
}
TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) {
gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(10, 10, 10));
canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(100, 100, 100));
canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(255, 255, 255));
SkBitmap source =
skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
SkBitmap result;
result.setConfig(SkBitmap::kA8_Config, 300, 200);
result.allocPixels();
EXPECT_TRUE(color_utils::ComputePrincipalComponentImage(source, &result));
uint8_t min_gl = 0;
uint8_t max_gl = 0;
Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
EXPECT_EQ(0, min_gl);
EXPECT_EQ(255, max_gl);
EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0)));
EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199)));
EXPECT_EQ(93U, SkColorGetA(result.getColor(150, 0)));
}