color_analysis_unittest.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "ui/gfx/color_analysis.h" 6 7#include <vector> 8 9#include "testing/gtest/include/gtest/gtest.h" 10#include "third_party/skia/include/core/SkBitmap.h" 11#include "third_party/skia/include/core/SkColor.h" 12#include "ui/gfx/canvas.h" 13#include "ui/gfx/color_utils.h" 14#include "ui/gfx/image/image.h" 15#include "ui/gfx/rect.h" 16 17namespace color_utils { 18 19const unsigned char k1x1White[] = { 20 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 21 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 22 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 23 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 24 0xde, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 25 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 26 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 27 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 28 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 29 0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x11, 0x15, 30 0x16, 0x1b, 0xaa, 0x58, 0x38, 0x76, 0x00, 0x00, 31 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 32 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72, 33 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 34 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 35 0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x49, 36 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 37 0xff, 0x3f, 0x00, 0x05, 0xfe, 0x02, 0xfe, 0xdc, 38 0xcc, 0x59, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x49, 39 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 40}; 41 42const unsigned char k1x3BlueWhite[] = { 43 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 44 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 45 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 46 0x08, 0x02, 0x00, 0x00, 0x00, 0xdd, 0xbf, 0xf2, 47 0xd5, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 48 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 49 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 50 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 51 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 52 0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x12, 0x01, 53 0x0a, 0x2c, 0xfd, 0x08, 0x64, 0x66, 0x00, 0x00, 54 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 55 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72, 56 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 57 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 58 0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x14, 0x49, 59 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 60 0xff, 0x3f, 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 61 0xc3, 0x7f, 0x00, 0x1e, 0xfd, 0x03, 0xff, 0xde, 62 0x72, 0x58, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x49, 63 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 64}; 65 66const unsigned char k1x3BlueRed[] = { 67 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 68 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 69 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 70 0x08, 0x02, 0x00, 0x00, 0x00, 0xdd, 0xbf, 0xf2, 71 0xd5, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 72 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 73 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 74 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 75 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 76 0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x12, 0x01, 77 0x07, 0x09, 0x03, 0xa2, 0xce, 0x6c, 0x00, 0x00, 78 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 79 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72, 80 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 81 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 82 0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x14, 0x49, 83 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xcf, 84 0xc0, 0xc0, 0xc4, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 85 0xf0, 0x1f, 0x00, 0x0c, 0x10, 0x02, 0x01, 0x2c, 86 0x8f, 0x8b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x49, 87 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 88}; 89 90const HSL kDefaultLowerBound = {-1, -1, 0.15}; 91const HSL kDefaultUpperBound = {-1, -1, 0.85}; 92 93// Creates a 1-dimensional png of the pixel colors found in |colors|. 94scoped_refptr<base::RefCountedMemory> CreateTestPNG( 95 const std::vector<SkColor>& colors) { 96 SkBitmap bitmap; 97 bitmap.setConfig(SkBitmap::kARGB_8888_Config, colors.size(), 1); 98 bitmap.allocPixels(); 99 100 SkAutoLockPixels lock(bitmap); 101 for (size_t i = 0; i < colors.size(); ++i) { 102 bitmap.eraseArea(SkIRect::MakeXYWH(i, 0, 1, 1), colors[i]); 103 } 104 return gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes(); 105} 106 107class MockKMeanImageSampler : public KMeanImageSampler { 108 public: 109 MockKMeanImageSampler() : current_result_index_(0) { 110 } 111 112 explicit MockKMeanImageSampler(const std::vector<int>& samples) 113 : prebaked_sample_results_(samples), 114 current_result_index_(0) { 115 } 116 117 virtual ~MockKMeanImageSampler() { 118 } 119 120 void AddSample(int sample) { 121 prebaked_sample_results_.push_back(sample); 122 } 123 124 virtual int GetSample(int width, int height) OVERRIDE { 125 if (current_result_index_ >= prebaked_sample_results_.size()) { 126 current_result_index_ = 0; 127 } 128 129 if (prebaked_sample_results_.empty()) { 130 return 0; 131 } 132 133 return prebaked_sample_results_[current_result_index_++]; 134 } 135 136 protected: 137 std::vector<int> prebaked_sample_results_; 138 size_t current_result_index_; 139}; 140 141// Return true if a color channel is approximately equal to an expected value. 142bool ChannelApproximatelyEqual(int expected, uint8_t channel) { 143 return (abs(expected - static_cast<int>(channel)) <= 1); 144} 145 146// Compute minimal and maximal graylevel (or alphalevel) of the input |bitmap|. 147// |bitmap| has to be allocated and configured to kA8_Config. 148void Calculate8bitBitmapMinMax(const SkBitmap& bitmap, 149 uint8_t* min_gl, 150 uint8_t* max_gl) { 151 SkAutoLockPixels bitmap_lock(bitmap); 152 DCHECK(bitmap.getPixels()); 153 DCHECK(bitmap.config() == SkBitmap::kA8_Config); 154 DCHECK(min_gl); 155 DCHECK(max_gl); 156 *min_gl = std::numeric_limits<uint8_t>::max(); 157 *max_gl = std::numeric_limits<uint8_t>::min(); 158 for (int y = 0; y < bitmap.height(); ++y) { 159 uint8_t* current_color = bitmap.getAddr8(0, y); 160 for (int x = 0; x < bitmap.width(); ++x, ++current_color) { 161 *min_gl = std::min(*min_gl, *current_color); 162 *max_gl = std::max(*max_gl, *current_color); 163 } 164 } 165} 166 167class ColorAnalysisTest : public testing::Test { 168}; 169 170TEST_F(ColorAnalysisTest, CalculatePNGKMeanAllWhite) { 171 MockKMeanImageSampler test_sampler; 172 test_sampler.AddSample(0); 173 174 scoped_refptr<base::RefCountedBytes> png( 175 new base::RefCountedBytes( 176 std::vector<unsigned char>( 177 k1x1White, 178 k1x1White + sizeof(k1x1White) / sizeof(unsigned char)))); 179 180 SkColor color = CalculateKMeanColorOfPNG( 181 png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); 182 183 EXPECT_EQ(color, SK_ColorWHITE); 184} 185 186TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhiteLightness) { 187 MockKMeanImageSampler test_sampler; 188 test_sampler.AddSample(0); 189 test_sampler.AddSample(1); 190 test_sampler.AddSample(2); 191 192 scoped_refptr<base::RefCountedBytes> png( 193 new base::RefCountedBytes( 194 std::vector<unsigned char>( 195 k1x3BlueWhite, 196 k1x3BlueWhite + sizeof(k1x3BlueWhite) / sizeof(unsigned char)))); 197 198 SkColor color = CalculateKMeanColorOfPNG( 199 png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); 200 201 EXPECT_EQ(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF), color); 202} 203 204TEST_F(ColorAnalysisTest, CalculatePNGKMeanPickMostCommon) { 205 MockKMeanImageSampler test_sampler; 206 test_sampler.AddSample(0); 207 test_sampler.AddSample(1); 208 test_sampler.AddSample(2); 209 210 scoped_refptr<base::RefCountedBytes> png( 211 new base::RefCountedBytes( 212 std::vector<unsigned char>( 213 k1x3BlueRed, 214 k1x3BlueRed + sizeof(k1x3BlueRed) / sizeof(unsigned char)))); 215 216 SkColor color = CalculateKMeanColorOfPNG( 217 png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); 218 219 EXPECT_EQ(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00), color); 220} 221 222TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreRedHue) { 223 MockKMeanImageSampler test_sampler; 224 test_sampler.AddSample(0); 225 test_sampler.AddSample(1); 226 test_sampler.AddSample(2); 227 228 std::vector<SkColor> colors(4, SK_ColorRED); 229 colors[1] = SK_ColorBLUE; 230 231 scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors); 232 233 HSL lower = {0.2, -1, 0.15}; 234 HSL upper = {0.8, -1, 0.85}; 235 SkColor color = CalculateKMeanColorOfPNG( 236 png, lower, upper, &test_sampler); 237 238 EXPECT_EQ(SK_ColorBLUE, color); 239} 240 241TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreGreySaturation) { 242 MockKMeanImageSampler test_sampler; 243 test_sampler.AddSample(0); 244 test_sampler.AddSample(1); 245 test_sampler.AddSample(2); 246 247 std::vector<SkColor> colors(4, SK_ColorGRAY); 248 colors[1] = SK_ColorBLUE; 249 250 scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors); 251 HSL lower = {-1, 0.3, -1}; 252 HSL upper = {-1, 1, -1}; 253 SkColor color = CalculateKMeanColorOfPNG( 254 png, lower, upper, &test_sampler); 255 256 EXPECT_EQ(SK_ColorBLUE, color); 257} 258 259TEST_F(ColorAnalysisTest, GridSampler) { 260 GridSampler sampler; 261 const int kWidth = 16; 262 const int kHeight = 16; 263 // Sample starts at 1,1. 264 EXPECT_EQ(1 + 1 * kWidth, sampler.GetSample(kWidth, kHeight)); 265 EXPECT_EQ(1 + 4 * kWidth, sampler.GetSample(kWidth, kHeight)); 266 EXPECT_EQ(1 + 7 * kWidth, sampler.GetSample(kWidth, kHeight)); 267 EXPECT_EQ(1 + 10 * kWidth, sampler.GetSample(kWidth, kHeight)); 268 // Step over by 3. 269 EXPECT_EQ(4 + 1 * kWidth, sampler.GetSample(kWidth, kHeight)); 270 EXPECT_EQ(4 + 4 * kWidth, sampler.GetSample(kWidth, kHeight)); 271 EXPECT_EQ(4 + 7 * kWidth, sampler.GetSample(kWidth, kHeight)); 272 EXPECT_EQ(4 + 10 * kWidth, sampler.GetSample(kWidth, kHeight)); 273} 274 275TEST_F(ColorAnalysisTest, FindClosestColor) { 276 // Empty image returns input color. 277 SkColor color = FindClosestColor(NULL, 0, 0, SK_ColorRED); 278 EXPECT_EQ(SK_ColorRED, color); 279 280 // Single color image returns that color. 281 SkBitmap bitmap; 282 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); 283 bitmap.allocPixels(); 284 bitmap.eraseColor(SK_ColorWHITE); 285 color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()), 286 bitmap.width(), 287 bitmap.height(), 288 SK_ColorRED); 289 EXPECT_EQ(SK_ColorWHITE, color); 290 291 // Write a black pixel into the image. A dark grey input pixel should match 292 // the black one in the image. 293 uint32_t* pixel = bitmap.getAddr32(0, 0); 294 *pixel = SK_ColorBLACK; 295 color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()), 296 bitmap.width(), 297 bitmap.height(), 298 SK_ColorDKGRAY); 299 EXPECT_EQ(SK_ColorBLACK, color); 300} 301 302TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) { 303 // Create a 16x16 bitmap to represent a favicon. 304 SkBitmap bitmap; 305 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); 306 bitmap.allocPixels(); 307 bitmap.eraseARGB(255, 100, 150, 200); 308 309 SkColor color = CalculateKMeanColorOfBitmap(bitmap); 310 EXPECT_EQ(255u, SkColorGetA(color)); 311 // Color values are not exactly equal due to reversal of premultiplied alpha. 312 EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color))); 313 EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color))); 314 EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color))); 315 316 // Test a bitmap with an alpha channel. 317 bitmap.eraseARGB(128, 100, 150, 200); 318 color = CalculateKMeanColorOfBitmap(bitmap); 319 320 // Alpha channel should be ignored for dominant color calculation. 321 EXPECT_EQ(255u, SkColorGetA(color)); 322 EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color))); 323 EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color))); 324 EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color))); 325} 326 327TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) { 328 SkBitmap bitmap; 329 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 200); 330 331 EXPECT_EQ(gfx::Matrix3F::Zeros(), ComputeColorCovariance(bitmap)); 332 bitmap.allocPixels(); 333 bitmap.eraseARGB(255, 50, 150, 200); 334 gfx::Matrix3F covariance = ComputeColorCovariance(bitmap); 335 // The answer should be all zeros. 336 EXPECT_TRUE(covariance == gfx::Matrix3F::Zeros()); 337} 338 339TEST_F(ColorAnalysisTest, ComputeColorCovarianceWithCanvas) { 340 gfx::Canvas canvas(gfx::Size(250, 200), 1.0f, true); 341 // The image consists of vertical stripes, with color bands set to 100 342 // in overlapping stripes 150 pixels wide. 343 canvas.FillRect(gfx::Rect(0, 0, 50, 200), SkColorSetRGB(100, 0, 0)); 344 canvas.FillRect(gfx::Rect(50, 0, 50, 200), SkColorSetRGB(100, 100, 0)); 345 canvas.FillRect(gfx::Rect(100, 0, 50, 200), SkColorSetRGB(100, 100, 100)); 346 canvas.FillRect(gfx::Rect(150, 0, 50, 200), SkColorSetRGB(0, 100, 100)); 347 canvas.FillRect(gfx::Rect(200, 0, 50, 200), SkColorSetRGB(0, 0, 100)); 348 349 SkBitmap bitmap = 350 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 351 gfx::Matrix3F covariance = ComputeColorCovariance(bitmap); 352 353 gfx::Matrix3F expected_covariance = gfx::Matrix3F::Zeros(); 354 expected_covariance.set(2400, 400, -1600, 355 400, 2400, 400, 356 -1600, 400, 2400); 357 EXPECT_EQ(expected_covariance, covariance); 358} 359 360TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) { 361 // The test runs color reduction on a single-colot image, where results are 362 // bound to be uninteresting. This is an important edge case, though. 363 SkBitmap source, result; 364 source.setConfig(SkBitmap::kARGB_8888_Config, 300, 200); 365 result.setConfig(SkBitmap::kA8_Config, 300, 200); 366 367 source.allocPixels(); 368 result.allocPixels(); 369 source.eraseARGB(255, 50, 150, 200); 370 371 gfx::Vector3dF transform(1.0f, .5f, 0.1f); 372 // This transform, if not scaled, should result in GL=145. 373 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 374 375 uint8_t min_gl = 0; 376 uint8_t max_gl = 0; 377 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 378 EXPECT_EQ(145, min_gl); 379 EXPECT_EQ(145, max_gl); 380 381 // Now scan requesting rescale. Expect all 0. 382 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 383 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 384 EXPECT_EQ(0, min_gl); 385 EXPECT_EQ(0, max_gl); 386 387 // Test cliping to upper limit. 388 transform.set_z(1.1f); 389 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 390 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 391 EXPECT_EQ(0xFF, min_gl); 392 EXPECT_EQ(0xFF, max_gl); 393 394 // Test cliping to upper limit. 395 transform.Scale(-1.0f); 396 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 397 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 398 EXPECT_EQ(0x0, min_gl); 399 EXPECT_EQ(0x0, max_gl); 400} 401 402TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) { 403 // Check with images with multiple colors. This is really different only when 404 // the result is scaled. 405 gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true); 406 407 // The image consists of vertical non-overlapping stripes 150 pixels wide. 408 canvas.FillRect(gfx::Rect(0, 0, 150, 200), SkColorSetRGB(0, 0, 0)); 409 canvas.FillRect(gfx::Rect(150, 0, 150, 200), SkColorSetRGB(255, 255, 255)); 410 SkBitmap source = 411 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 412 SkBitmap result; 413 result.setConfig(SkBitmap::kA8_Config, 300, 200); 414 result.allocPixels(); 415 416 gfx::Vector3dF transform(1.0f, 0.5f, 0.1f); 417 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 418 uint8_t min_gl = 0; 419 uint8_t max_gl = 0; 420 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 421 422 EXPECT_EQ(0, min_gl); 423 EXPECT_EQ(255, max_gl); 424 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0))); 425 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199))); 426 427 // Reverse test. 428 transform.Scale(-1.0f); 429 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 430 min_gl = 0; 431 max_gl = 0; 432 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 433 434 EXPECT_EQ(0, min_gl); 435 EXPECT_EQ(255, max_gl); 436 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(0, 0))); 437 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199))); 438} 439 440TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) { 441 // Check with images with multiple colors. This is really different only when 442 // the result is scaled. 443 gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true); 444 445 // The image consists of vertical non-overlapping stripes 100 pixels wide. 446 canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(100, 0, 0)); 447 canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(0, 255, 0)); 448 canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(0, 0, 128)); 449 SkBitmap source = 450 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 451 SkBitmap result; 452 result.setConfig(SkBitmap::kA8_Config, 300, 200); 453 result.allocPixels(); 454 455 gfx::Vector3dF transform(1.0f, 0.5f, 0.1f); 456 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 457 uint8_t min_gl = 0; 458 uint8_t max_gl = 0; 459 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 460 EXPECT_EQ(12, min_gl); 461 EXPECT_EQ(127, max_gl); 462 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199))); 463 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0))); 464 EXPECT_EQ(100U, SkColorGetA(result.getColor(0, 0))); 465 466 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 467 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 468 EXPECT_EQ(0, min_gl); 469 EXPECT_EQ(255, max_gl); 470 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199))); 471 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0))); 472 EXPECT_EQ(193U, SkColorGetA(result.getColor(0, 0))); 473} 474 475TEST_F(ColorAnalysisTest, ComputePrincipalComponentImageNotComputable) { 476 SkBitmap source, result; 477 source.setConfig(SkBitmap::kARGB_8888_Config, 300, 200); 478 result.setConfig(SkBitmap::kA8_Config, 300, 200); 479 480 source.allocPixels(); 481 result.allocPixels(); 482 source.eraseARGB(255, 50, 150, 200); 483 484 // This computation should fail since all colors always vary together. 485 EXPECT_FALSE(ComputePrincipalComponentImage(source, &result)); 486} 487 488TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) { 489 gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true); 490 491 // The image consists of vertical non-overlapping stripes 100 pixels wide. 492 canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(10, 10, 10)); 493 canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(100, 100, 100)); 494 canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(255, 255, 255)); 495 SkBitmap source = 496 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 497 SkBitmap result; 498 result.setConfig(SkBitmap::kA8_Config, 300, 200); 499 result.allocPixels(); 500 501 // This computation should fail since all colors always vary together. 502 EXPECT_TRUE(ComputePrincipalComponentImage(source, &result)); 503 504 uint8_t min_gl = 0; 505 uint8_t max_gl = 0; 506 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 507 508 EXPECT_EQ(0, min_gl); 509 EXPECT_EQ(255, max_gl); 510 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0))); 511 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199))); 512 EXPECT_EQ(93U, SkColorGetA(result.getColor(150, 0))); 513} 514 515} // namespace color_utils 516