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.allocN32Pixels(colors.size(), 1); 98 99 SkAutoLockPixels lock(bitmap); 100 for (size_t i = 0; i < colors.size(); ++i) { 101 bitmap.eraseArea(SkIRect::MakeXYWH(i, 0, 1, 1), colors[i]); 102 } 103 return gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes(); 104} 105 106class MockKMeanImageSampler : public KMeanImageSampler { 107 public: 108 MockKMeanImageSampler() : current_result_index_(0) { 109 } 110 111 explicit MockKMeanImageSampler(const std::vector<int>& samples) 112 : prebaked_sample_results_(samples), 113 current_result_index_(0) { 114 } 115 116 virtual ~MockKMeanImageSampler() { 117 } 118 119 void AddSample(int sample) { 120 prebaked_sample_results_.push_back(sample); 121 } 122 123 virtual int GetSample(int width, int height) OVERRIDE { 124 if (current_result_index_ >= prebaked_sample_results_.size()) { 125 current_result_index_ = 0; 126 } 127 128 if (prebaked_sample_results_.empty()) { 129 return 0; 130 } 131 132 return prebaked_sample_results_[current_result_index_++]; 133 } 134 135 protected: 136 std::vector<int> prebaked_sample_results_; 137 size_t current_result_index_; 138}; 139 140// Return true if a color channel is approximately equal to an expected value. 141bool ChannelApproximatelyEqual(int expected, uint8_t channel) { 142 return (abs(expected - static_cast<int>(channel)) <= 1); 143} 144 145// Compute minimal and maximal graylevel (or alphalevel) of the input |bitmap|. 146// |bitmap| has to be allocated and configured to kA8_Config. 147void Calculate8bitBitmapMinMax(const SkBitmap& bitmap, 148 uint8_t* min_gl, 149 uint8_t* max_gl) { 150 SkAutoLockPixels bitmap_lock(bitmap); 151 DCHECK(bitmap.getPixels()); 152 DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType); 153 DCHECK(min_gl); 154 DCHECK(max_gl); 155 *min_gl = std::numeric_limits<uint8_t>::max(); 156 *max_gl = std::numeric_limits<uint8_t>::min(); 157 for (int y = 0; y < bitmap.height(); ++y) { 158 uint8_t* current_color = bitmap.getAddr8(0, y); 159 for (int x = 0; x < bitmap.width(); ++x, ++current_color) { 160 *min_gl = std::min(*min_gl, *current_color); 161 *max_gl = std::max(*max_gl, *current_color); 162 } 163 } 164} 165 166class ColorAnalysisTest : public testing::Test { 167}; 168 169TEST_F(ColorAnalysisTest, CalculatePNGKMeanAllWhite) { 170 MockKMeanImageSampler test_sampler; 171 test_sampler.AddSample(0); 172 173 scoped_refptr<base::RefCountedBytes> png( 174 new base::RefCountedBytes( 175 std::vector<unsigned char>( 176 k1x1White, 177 k1x1White + sizeof(k1x1White) / sizeof(unsigned char)))); 178 179 SkColor color = CalculateKMeanColorOfPNG( 180 png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); 181 182 EXPECT_EQ(color, SK_ColorWHITE); 183} 184 185TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhiteLightness) { 186 MockKMeanImageSampler test_sampler; 187 test_sampler.AddSample(0); 188 test_sampler.AddSample(1); 189 test_sampler.AddSample(2); 190 191 scoped_refptr<base::RefCountedBytes> png( 192 new base::RefCountedBytes( 193 std::vector<unsigned char>( 194 k1x3BlueWhite, 195 k1x3BlueWhite + sizeof(k1x3BlueWhite) / sizeof(unsigned char)))); 196 197 SkColor color = CalculateKMeanColorOfPNG( 198 png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); 199 200 EXPECT_EQ(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF), color); 201} 202 203TEST_F(ColorAnalysisTest, CalculatePNGKMeanPickMostCommon) { 204 MockKMeanImageSampler test_sampler; 205 test_sampler.AddSample(0); 206 test_sampler.AddSample(1); 207 test_sampler.AddSample(2); 208 209 scoped_refptr<base::RefCountedBytes> png( 210 new base::RefCountedBytes( 211 std::vector<unsigned char>( 212 k1x3BlueRed, 213 k1x3BlueRed + sizeof(k1x3BlueRed) / sizeof(unsigned char)))); 214 215 SkColor color = CalculateKMeanColorOfPNG( 216 png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler); 217 218 EXPECT_EQ(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00), color); 219} 220 221TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreRedHue) { 222 MockKMeanImageSampler test_sampler; 223 test_sampler.AddSample(0); 224 test_sampler.AddSample(1); 225 test_sampler.AddSample(2); 226 227 std::vector<SkColor> colors(4, SK_ColorRED); 228 colors[1] = SK_ColorBLUE; 229 230 scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors); 231 232 HSL lower = {0.2, -1, 0.15}; 233 HSL upper = {0.8, -1, 0.85}; 234 SkColor color = CalculateKMeanColorOfPNG( 235 png, lower, upper, &test_sampler); 236 237 EXPECT_EQ(SK_ColorBLUE, color); 238} 239 240TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreGreySaturation) { 241 MockKMeanImageSampler test_sampler; 242 test_sampler.AddSample(0); 243 test_sampler.AddSample(1); 244 test_sampler.AddSample(2); 245 246 std::vector<SkColor> colors(4, SK_ColorGRAY); 247 colors[1] = SK_ColorBLUE; 248 249 scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors); 250 HSL lower = {-1, 0.3, -1}; 251 HSL upper = {-1, 1, -1}; 252 SkColor color = CalculateKMeanColorOfPNG( 253 png, lower, upper, &test_sampler); 254 255 EXPECT_EQ(SK_ColorBLUE, color); 256} 257 258TEST_F(ColorAnalysisTest, GridSampler) { 259 GridSampler sampler; 260 const int kWidth = 16; 261 const int kHeight = 16; 262 // Sample starts at 1,1. 263 EXPECT_EQ(1 + 1 * kWidth, sampler.GetSample(kWidth, kHeight)); 264 EXPECT_EQ(1 + 4 * kWidth, sampler.GetSample(kWidth, kHeight)); 265 EXPECT_EQ(1 + 7 * kWidth, sampler.GetSample(kWidth, kHeight)); 266 EXPECT_EQ(1 + 10 * kWidth, sampler.GetSample(kWidth, kHeight)); 267 // Step over by 3. 268 EXPECT_EQ(4 + 1 * kWidth, sampler.GetSample(kWidth, kHeight)); 269 EXPECT_EQ(4 + 4 * kWidth, sampler.GetSample(kWidth, kHeight)); 270 EXPECT_EQ(4 + 7 * kWidth, sampler.GetSample(kWidth, kHeight)); 271 EXPECT_EQ(4 + 10 * kWidth, sampler.GetSample(kWidth, kHeight)); 272} 273 274TEST_F(ColorAnalysisTest, FindClosestColor) { 275 // Empty image returns input color. 276 SkColor color = FindClosestColor(NULL, 0, 0, SK_ColorRED); 277 EXPECT_EQ(SK_ColorRED, color); 278 279 // Single color image returns that color. 280 SkBitmap bitmap; 281 bitmap.allocN32Pixels(16, 16); 282 bitmap.eraseColor(SK_ColorWHITE); 283 color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()), 284 bitmap.width(), 285 bitmap.height(), 286 SK_ColorRED); 287 EXPECT_EQ(SK_ColorWHITE, color); 288 289 // Write a black pixel into the image. A dark grey input pixel should match 290 // the black one in the image. 291 uint32_t* pixel = bitmap.getAddr32(0, 0); 292 *pixel = SK_ColorBLACK; 293 color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()), 294 bitmap.width(), 295 bitmap.height(), 296 SK_ColorDKGRAY); 297 EXPECT_EQ(SK_ColorBLACK, color); 298} 299 300TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) { 301 // Create a 16x16 bitmap to represent a favicon. 302 SkBitmap bitmap; 303 bitmap.allocN32Pixels(16, 16); 304 bitmap.eraseARGB(255, 100, 150, 200); 305 306 SkColor color = CalculateKMeanColorOfBitmap(bitmap); 307 EXPECT_EQ(255u, SkColorGetA(color)); 308 // Color values are not exactly equal due to reversal of premultiplied alpha. 309 EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color))); 310 EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color))); 311 EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color))); 312 313 // Test a bitmap with an alpha channel. 314 bitmap.eraseARGB(128, 100, 150, 200); 315 color = CalculateKMeanColorOfBitmap(bitmap); 316 317 // Alpha channel should be ignored for dominant color calculation. 318 EXPECT_EQ(255u, SkColorGetA(color)); 319 EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color))); 320 EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color))); 321 EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color))); 322} 323 324TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) { 325 SkBitmap bitmap; 326 bitmap.setInfo(SkImageInfo::MakeN32Premul(100, 200)); 327 328 EXPECT_EQ(gfx::Matrix3F::Zeros(), ComputeColorCovariance(bitmap)); 329 bitmap.allocPixels(); 330 bitmap.eraseARGB(255, 50, 150, 200); 331 gfx::Matrix3F covariance = ComputeColorCovariance(bitmap); 332 // The answer should be all zeros. 333 EXPECT_TRUE(covariance == gfx::Matrix3F::Zeros()); 334} 335 336TEST_F(ColorAnalysisTest, ComputeColorCovarianceWithCanvas) { 337 gfx::Canvas canvas(gfx::Size(250, 200), 1.0f, true); 338 // The image consists of vertical stripes, with color bands set to 100 339 // in overlapping stripes 150 pixels wide. 340 canvas.FillRect(gfx::Rect(0, 0, 50, 200), SkColorSetRGB(100, 0, 0)); 341 canvas.FillRect(gfx::Rect(50, 0, 50, 200), SkColorSetRGB(100, 100, 0)); 342 canvas.FillRect(gfx::Rect(100, 0, 50, 200), SkColorSetRGB(100, 100, 100)); 343 canvas.FillRect(gfx::Rect(150, 0, 50, 200), SkColorSetRGB(0, 100, 100)); 344 canvas.FillRect(gfx::Rect(200, 0, 50, 200), SkColorSetRGB(0, 0, 100)); 345 346 SkBitmap bitmap = 347 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 348 gfx::Matrix3F covariance = ComputeColorCovariance(bitmap); 349 350 gfx::Matrix3F expected_covariance = gfx::Matrix3F::Zeros(); 351 expected_covariance.set(2400, 400, -1600, 352 400, 2400, 400, 353 -1600, 400, 2400); 354 EXPECT_EQ(expected_covariance, covariance); 355} 356 357TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) { 358 // The test runs color reduction on a single-colot image, where results are 359 // bound to be uninteresting. This is an important edge case, though. 360 SkBitmap source, result; 361 source.allocN32Pixels(300, 200); 362 result.allocPixels(SkImageInfo::MakeA8(300, 200)); 363 364 source.eraseARGB(255, 50, 150, 200); 365 366 gfx::Vector3dF transform(1.0f, .5f, 0.1f); 367 // This transform, if not scaled, should result in GL=145. 368 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 369 370 uint8_t min_gl = 0; 371 uint8_t max_gl = 0; 372 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 373 EXPECT_EQ(145, min_gl); 374 EXPECT_EQ(145, max_gl); 375 376 // Now scan requesting rescale. Expect all 0. 377 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 378 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 379 EXPECT_EQ(0, min_gl); 380 EXPECT_EQ(0, max_gl); 381 382 // Test cliping to upper limit. 383 transform.set_z(1.1f); 384 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 385 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 386 EXPECT_EQ(0xFF, min_gl); 387 EXPECT_EQ(0xFF, max_gl); 388 389 // Test cliping to upper limit. 390 transform.Scale(-1.0f); 391 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 392 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 393 EXPECT_EQ(0x0, min_gl); 394 EXPECT_EQ(0x0, max_gl); 395} 396 397TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) { 398 // Check with images with multiple colors. This is really different only when 399 // the result is scaled. 400 gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true); 401 402 // The image consists of vertical non-overlapping stripes 150 pixels wide. 403 canvas.FillRect(gfx::Rect(0, 0, 150, 200), SkColorSetRGB(0, 0, 0)); 404 canvas.FillRect(gfx::Rect(150, 0, 150, 200), SkColorSetRGB(255, 255, 255)); 405 SkBitmap source = 406 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 407 SkBitmap result; 408 result.allocPixels(SkImageInfo::MakeA8(300, 200)); 409 410 gfx::Vector3dF transform(1.0f, 0.5f, 0.1f); 411 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 412 uint8_t min_gl = 0; 413 uint8_t max_gl = 0; 414 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 415 416 EXPECT_EQ(0, min_gl); 417 EXPECT_EQ(255, max_gl); 418 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0))); 419 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199))); 420 421 // Reverse test. 422 transform.Scale(-1.0f); 423 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 424 min_gl = 0; 425 max_gl = 0; 426 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 427 428 EXPECT_EQ(0, min_gl); 429 EXPECT_EQ(255, max_gl); 430 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(0, 0))); 431 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199))); 432} 433 434TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) { 435 // Check with images with multiple colors. This is really different only when 436 // the result is scaled. 437 gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true); 438 439 // The image consists of vertical non-overlapping stripes 100 pixels wide. 440 canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(100, 0, 0)); 441 canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(0, 255, 0)); 442 canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(0, 0, 128)); 443 SkBitmap source = 444 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 445 SkBitmap result; 446 result.allocPixels(SkImageInfo::MakeA8(300, 200)); 447 448 gfx::Vector3dF transform(1.0f, 0.5f, 0.1f); 449 EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result)); 450 uint8_t min_gl = 0; 451 uint8_t max_gl = 0; 452 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 453 EXPECT_EQ(12, min_gl); 454 EXPECT_EQ(127, max_gl); 455 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199))); 456 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0))); 457 EXPECT_EQ(100U, SkColorGetA(result.getColor(0, 0))); 458 459 EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result)); 460 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 461 EXPECT_EQ(0, min_gl); 462 EXPECT_EQ(255, max_gl); 463 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199))); 464 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0))); 465 EXPECT_EQ(193U, SkColorGetA(result.getColor(0, 0))); 466} 467 468TEST_F(ColorAnalysisTest, ComputePrincipalComponentImageNotComputable) { 469 SkBitmap source, result; 470 source.allocN32Pixels(300, 200); 471 result.allocPixels(SkImageInfo::MakeA8(300, 200)); 472 473 source.eraseARGB(255, 50, 150, 200); 474 475 // This computation should fail since all colors always vary together. 476 EXPECT_FALSE(ComputePrincipalComponentImage(source, &result)); 477} 478 479TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) { 480 gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true); 481 482 // The image consists of vertical non-overlapping stripes 100 pixels wide. 483 canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(10, 10, 10)); 484 canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(100, 100, 100)); 485 canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(255, 255, 255)); 486 SkBitmap source = 487 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); 488 SkBitmap result; 489 result.allocPixels(SkImageInfo::MakeA8(300, 200)); 490 491 // This computation should fail since all colors always vary together. 492 EXPECT_TRUE(ComputePrincipalComponentImage(source, &result)); 493 494 uint8_t min_gl = 0; 495 uint8_t max_gl = 0; 496 Calculate8bitBitmapMinMax(result, &min_gl, &max_gl); 497 498 EXPECT_EQ(0, min_gl); 499 EXPECT_EQ(255, max_gl); 500 EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0))); 501 EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199))); 502 EXPECT_EQ(93U, SkColorGetA(result.getColor(150, 0))); 503} 504 505} // namespace color_utils 506