601089f39cd476e2a78485c539ae1a21df276ebe
[platform/framework/web/crosswalk.git] / src / ui / gfx / color_analysis_unittest.cc
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|.
94 scoped_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
106 class 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.
141 bool 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.
147 void 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
166 class ColorAnalysisTest : public testing::Test {
167 };
168
169 TEST_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
185 TEST_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
203 TEST_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
221 TEST_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
240 TEST_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
258 TEST_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
274 TEST_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
300 TEST_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
324 TEST_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
336 TEST_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
357 TEST_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
397 TEST_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
434 TEST_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
468 TEST_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
479 TEST_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