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