• 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/color_utils.h"
14 #include "ui/gfx/image/image.h"
15 #include "ui/gfx/rect.h"
16 
17 namespace color_utils {
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 const HSL kDefaultLowerBound = {-1, -1, 0.15};
91 const HSL kDefaultUpperBound = {-1, -1, 0.85};
92 
93 // Creates a 1-dimensional png of the pixel colors found in |colors|.
CreateTestPNG(const std::vector<SkColor> & colors)94 scoped_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 
107 class MockKMeanImageSampler : public KMeanImageSampler {
108  public:
MockKMeanImageSampler()109   MockKMeanImageSampler() : current_result_index_(0) {
110   }
111 
MockKMeanImageSampler(const std::vector<int> & samples)112   explicit MockKMeanImageSampler(const std::vector<int>& samples)
113       : prebaked_sample_results_(samples),
114         current_result_index_(0) {
115   }
116 
~MockKMeanImageSampler()117   virtual ~MockKMeanImageSampler() {
118   }
119 
AddSample(int sample)120   void AddSample(int sample) {
121     prebaked_sample_results_.push_back(sample);
122   }
123 
GetSample(int width,int height)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.
ChannelApproximatelyEqual(int expected,uint8_t channel)142 bool 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.
Calculate8bitBitmapMinMax(const SkBitmap & bitmap,uint8_t * min_gl,uint8_t * max_gl)148 void 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 
167 class ColorAnalysisTest : public testing::Test {
168 };
169 
TEST_F(ColorAnalysisTest,CalculatePNGKMeanAllWhite)170 TEST_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 
TEST_F(ColorAnalysisTest,CalculatePNGKMeanIgnoreWhiteLightness)186 TEST_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 
TEST_F(ColorAnalysisTest,CalculatePNGKMeanPickMostCommon)204 TEST_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 
TEST_F(ColorAnalysisTest,CalculatePNGKMeanIgnoreRedHue)222 TEST_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 
TEST_F(ColorAnalysisTest,CalculatePNGKMeanIgnoreGreySaturation)241 TEST_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 
TEST_F(ColorAnalysisTest,GridSampler)259 TEST_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 
TEST_F(ColorAnalysisTest,FindClosestColor)275 TEST_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 
TEST_F(ColorAnalysisTest,CalculateKMeanColorOfBitmap)302 TEST_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 
TEST_F(ColorAnalysisTest,ComputeColorCovarianceTrivial)327 TEST_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 
TEST_F(ColorAnalysisTest,ComputeColorCovarianceWithCanvas)339 TEST_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 
TEST_F(ColorAnalysisTest,ApplyColorReductionSingleColor)360 TEST_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 
TEST_F(ColorAnalysisTest,ApplyColorReductionBlackAndWhite)402 TEST_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 
TEST_F(ColorAnalysisTest,ApplyColorReductionMultiColor)440 TEST_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 
TEST_F(ColorAnalysisTest,ComputePrincipalComponentImageNotComputable)475 TEST_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 
TEST_F(ColorAnalysisTest,ComputePrincipalComponentImage)488 TEST_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