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