15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/color_analysis.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkColor.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/canvas.h"
1346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/gfx/color_utils.h"
1446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/gfx/image/image.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/rect.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)namespace color_utils {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const unsigned char k1x1White[] = {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xde, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47,
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00,
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00,
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74,
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x11, 0x15,
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x16, 0x1b, 0xaa, 0x58, 0x38, 0x76, 0x00, 0x00,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x49,
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff,
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xff, 0x3f, 0x00, 0x05, 0xfe, 0x02, 0xfe, 0xdc,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xcc, 0x59, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x49,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const unsigned char k1x3BlueWhite[] = {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x08, 0x02, 0x00, 0x00, 0x00, 0xdd, 0xbf, 0xf2,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xd5, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00,
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x12, 0x01,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x0a, 0x2c, 0xfd, 0x08, 0x64, 0x66, 0x00, 0x00,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f,
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72,
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69,
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57,
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x14, 0x49,
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xff, 0x3f, 0x13, 0x03, 0x03, 0x03, 0x03, 0x03,
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xc3, 0x7f, 0x00, 0x1e, 0xfd, 0x03, 0xff, 0xde,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x72, 0x58, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x49,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const unsigned char k1x3BlueRed[] = {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x08, 0x02, 0x00, 0x00, 0x00, 0xdd, 0xbf, 0xf2,
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xd5, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00,
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00,
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74,
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x49, 0x4d, 0x45, 0x07, 0xdb, 0x02, 0x12, 0x01,
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x07, 0x09, 0x03, 0xa2, 0xce, 0x6c, 0x00, 0x00,
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f,
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x43, 0x72,
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69,
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57,
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x81, 0x0e, 0x17, 0x00, 0x00, 0x00, 0x14, 0x49,
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xcf,
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xc0, 0xc0, 0xc4, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0xf0, 0x1f, 0x00, 0x0c, 0x10, 0x02, 0x01, 0x2c,
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x8f, 0x8b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x49,
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const HSL kDefaultLowerBound = {-1, -1, 0.15};
9146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const HSL kDefaultUpperBound = {-1, -1, 0.85};
9246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
9346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Creates a 1-dimensional png of the pixel colors found in |colors|.
9446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)scoped_refptr<base::RefCountedMemory> CreateTestPNG(
9546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    const std::vector<SkColor>& colors) {
9646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkBitmap bitmap;
97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bitmap.allocN32Pixels(colors.size(), 1);
9846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
9946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkAutoLockPixels lock(bitmap);
10046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  for (size_t i = 0; i < colors.size(); ++i) {
10146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    bitmap.eraseArea(SkIRect::MakeXYWH(i, 0, 1, 1), colors[i]);
10246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
10346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes();
10446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
10546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)class MockKMeanImageSampler : public KMeanImageSampler {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler() : current_result_index_(0) {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit MockKMeanImageSampler(const std::vector<int>& samples)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : prebaked_sample_results_(samples),
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_result_index_(0) {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~MockKMeanImageSampler() {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddSample(int sample) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prebaked_sample_results_.push_back(sample);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual int GetSample(int width, int height) OVERRIDE {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (current_result_index_ >= prebaked_sample_results_.size()) {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_result_index_ = 0;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (prebaked_sample_results_.empty()) {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return prebaked_sample_results_[current_result_index_++];
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<int> prebaked_sample_results_;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t current_result_index_;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Return true if a color channel is approximately equal to an expected value.
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ChannelApproximatelyEqual(int expected, uint8_t channel) {
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (abs(expected - static_cast<int>(channel)) <= 1);
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Compute minimal and maximal graylevel (or alphalevel) of the input |bitmap|.
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |bitmap| has to be allocated and configured to kA8_Config.
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void Calculate8bitBitmapMinMax(const SkBitmap& bitmap,
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               uint8_t* min_gl,
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               uint8_t* max_gl) {
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkAutoLockPixels bitmap_lock(bitmap);
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(bitmap.getPixels());
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType);
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(min_gl);
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(max_gl);
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *min_gl = std::numeric_limits<uint8_t>::max();
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *max_gl = std::numeric_limits<uint8_t>::min();
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int y = 0; y < bitmap.height(); ++y) {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    uint8_t* current_color = bitmap.getAddr8(0, y);
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int x = 0; x < bitmap.width(); ++x, ++current_color) {
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *min_gl = std::min(*min_gl, *current_color);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *max_gl = std::max(*max_gl, *current_color);
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ColorAnalysisTest : public testing::Test {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanAllWhite) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler test_sampler;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(0);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<base::RefCountedBytes> png(
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new base::RefCountedBytes(
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::vector<unsigned char>(
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              k1x1White,
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              k1x1White + sizeof(k1x1White) / sizeof(unsigned char))));
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkColor color = CalculateKMeanColorOfPNG(
18046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(color, SK_ColorWHITE);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhiteLightness) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler test_sampler;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(0);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(1);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(2);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<base::RefCountedBytes> png(
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     new base::RefCountedBytes(
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         std::vector<unsigned char>(
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueWhite,
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueWhite + sizeof(k1x3BlueWhite) / sizeof(unsigned char))));
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkColor color = CalculateKMeanColorOfPNG(
19846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_EQ(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF), color);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanPickMostCommon) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler test_sampler;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(0);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(1);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(2);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<base::RefCountedBytes> png(
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     new base::RefCountedBytes(
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         std::vector<unsigned char>(
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueRed,
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueRed + sizeof(k1x3BlueRed) / sizeof(unsigned char))));
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkColor color = CalculateKMeanColorOfPNG(
21646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      png, kDefaultLowerBound, kDefaultUpperBound, &test_sampler);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_EQ(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00), color);
21946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
22046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
22146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreRedHue) {
22246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  MockKMeanImageSampler test_sampler;
22346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  test_sampler.AddSample(0);
22446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  test_sampler.AddSample(1);
22546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  test_sampler.AddSample(2);
22646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
22746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  std::vector<SkColor> colors(4, SK_ColorRED);
22846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  colors[1] = SK_ColorBLUE;
22946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
23046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors);
23146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
23246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  HSL lower = {0.2, -1, 0.15};
23346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  HSL upper = {0.8, -1, 0.85};
23446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkColor color = CalculateKMeanColorOfPNG(
23546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      png, lower, upper, &test_sampler);
23646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
23746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_EQ(SK_ColorBLUE, color);
23846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
23946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
24046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreGreySaturation) {
24146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  MockKMeanImageSampler test_sampler;
24246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  test_sampler.AddSample(0);
24346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  test_sampler.AddSample(1);
24446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  test_sampler.AddSample(2);
24546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
24646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  std::vector<SkColor> colors(4, SK_ColorGRAY);
24746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  colors[1] = SK_ColorBLUE;
24846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
24946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  scoped_refptr<base::RefCountedMemory> png = CreateTestPNG(colors);
25046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  HSL lower = {-1, 0.3, -1};
25146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  HSL upper = {-1, 1, -1};
25246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkColor color = CalculateKMeanColorOfPNG(
25346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      png, lower, upper, &test_sampler);
25446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
25546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_EQ(SK_ColorBLUE, color);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, GridSampler) {
25946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  GridSampler sampler;
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kWidth = 16;
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kHeight = 16;
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Sample starts at 1,1.
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 1 * kWidth, sampler.GetSample(kWidth, kHeight));
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 4 * kWidth, sampler.GetSample(kWidth, kHeight));
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 7 * kWidth, sampler.GetSample(kWidth, kHeight));
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 10 * kWidth, sampler.GetSample(kWidth, kHeight));
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Step over by 3.
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 1 * kWidth, sampler.GetSample(kWidth, kHeight));
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 4 * kWidth, sampler.GetSample(kWidth, kHeight));
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 7 * kWidth, sampler.GetSample(kWidth, kHeight));
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 10 * kWidth, sampler.GetSample(kWidth, kHeight));
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, FindClosestColor) {
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Empty image returns input color.
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor color = FindClosestColor(NULL, 0, 0, SK_ColorRED);
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(SK_ColorRED, color);
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Single color image returns that color.
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap;
281116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bitmap.allocN32Pixels(16, 16);
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.eraseColor(SK_ColorWHITE);
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()),
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.width(),
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.height(),
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           SK_ColorRED);
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(SK_ColorWHITE, color);
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Write a black pixel into the image. A dark grey input pixel should match
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the black one in the image.
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint32_t* pixel = bitmap.getAddr32(0, 0);
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *pixel = SK_ColorBLACK;
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()),
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.width(),
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.height(),
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           SK_ColorDKGRAY);
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(SK_ColorBLACK, color);
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) {
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create a 16x16 bitmap to represent a favicon.
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap;
303116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bitmap.allocN32Pixels(16, 16);
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.eraseARGB(255, 100, 150, 200);
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
30646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SkColor color = CalculateKMeanColorOfBitmap(bitmap);
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255u, SkColorGetA(color));
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Color values are not exactly equal due to reversal of premultiplied alpha.
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Test a bitmap with an alpha channel.
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.eraseARGB(128, 100, 150, 200);
31546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  color = CalculateKMeanColorOfBitmap(bitmap);
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Alpha channel should be ignored for dominant color calculation.
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255u, SkColorGetA(color));
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) {
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap;
326116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bitmap.setInfo(SkImageInfo::MakeN32Premul(100, 200));
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
32846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_EQ(gfx::Matrix3F::Zeros(), ComputeColorCovariance(bitmap));
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.allocPixels();
330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  bitmap.eraseARGB(255, 50, 150, 200);
33146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  gfx::Matrix3F covariance = ComputeColorCovariance(bitmap);
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The answer should be all zeros.
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(covariance == gfx::Matrix3F::Zeros());
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputeColorCovarianceWithCanvas) {
33768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(250, 200), 1.0f, true);
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical stripes, with color bands set to 100
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // in overlapping stripes 150 pixels wide.
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 50, 200), SkColorSetRGB(100, 0, 0));
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(50, 0, 50, 200), SkColorSetRGB(100, 100, 0));
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 0, 50, 200), SkColorSetRGB(100, 100, 100));
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(150, 0, 50, 200), SkColorSetRGB(0, 100, 100));
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(200, 0, 50, 200), SkColorSetRGB(0, 0, 100));
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap =
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
34846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  gfx::Matrix3F covariance = ComputeColorCovariance(bitmap);
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Matrix3F expected_covariance = gfx::Matrix3F::Zeros();
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  expected_covariance.set(2400, 400, -1600,
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          400, 2400, 400,
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          -1600, 400, 2400);
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(expected_covariance, covariance);
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) {
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The test runs color reduction on a single-colot image, where results are
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bound to be uninteresting. This is an important edge case, though.
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source, result;
361116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  source.allocN32Pixels(300, 200);
362116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  result.allocPixels(SkImageInfo::MakeA8(300, 200));
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
364cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  source.eraseARGB(255, 50, 150, 200);
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Vector3dF transform(1.0f, .5f, 0.1f);
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This transform, if not scaled, should result in GL=145.
36846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(145, min_gl);
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(145, max_gl);
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Now scan requesting rescale. Expect all 0.
37746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, max_gl);
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Test cliping to upper limit.
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  transform.set_z(1.1f);
38446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0xFF, min_gl);
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0xFF, max_gl);
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Test cliping to upper limit.
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  transform.Scale(-1.0f);
39146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0x0, min_gl);
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0x0, max_gl);
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) {
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check with images with multiple colors. This is really different only when
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the result is scaled.
40068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical non-overlapping stripes 150 pixels wide.
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 150, 200), SkColorSetRGB(0, 0, 0));
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(150, 0, 150, 200), SkColorSetRGB(255, 255, 255));
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source =
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap result;
408116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  result.allocPixels(SkImageInfo::MakeA8(300, 200));
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
41146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0)));
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199)));
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Reverse test.
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  transform.Scale(-1.0f);
42346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  min_gl = 0;
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  max_gl = 0;
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(0, 0)));
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) {
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check with images with multiple colors. This is really different only when
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the result is scaled.
43768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical non-overlapping stripes 100 pixels wide.
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(100, 0, 0));
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(0, 255, 0));
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(0, 0, 128));
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source =
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap result;
446116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  result.allocPixels(SkImageInfo::MakeA8(300, 200));
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
44946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(12, min_gl);
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(127, max_gl);
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(100U, SkColorGetA(result.getColor(0, 0)));
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
45946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(193U, SkColorGetA(result.getColor(0, 0)));
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputePrincipalComponentImageNotComputable) {
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source, result;
470116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  source.allocN32Pixels(300, 200);
471116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  result.allocPixels(SkImageInfo::MakeA8(300, 200));
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
473cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  source.eraseARGB(255, 50, 150, 200);
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This computation should fail since all colors always vary together.
47646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_FALSE(ComputePrincipalComponentImage(source, &result));
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) {
48068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical non-overlapping stripes 100 pixels wide.
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(10, 10, 10));
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(100, 100, 100));
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(255, 255, 255));
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source =
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap result;
489116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  result.allocPixels(SkImageInfo::MakeA8(300, 200));
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This computation should fail since all colors always vary together.
49246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  EXPECT_TRUE(ComputePrincipalComponentImage(source, &result));
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0)));
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199)));
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(93U, SkColorGetA(result.getColor(150, 0)));
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
50446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
50546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}  // namespace color_utils
506