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"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/rect.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using color_utils::FindClosestColor;
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
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)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockKMeanImageSampler : public color_utils::KMeanImageSampler {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler() : current_result_index_(0) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit MockKMeanImageSampler(const std::vector<int>& samples)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : prebaked_sample_results_(samples),
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_result_index_(0) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~MockKMeanImageSampler() {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddSample(int sample) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prebaked_sample_results_.push_back(sample);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Reset() {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prebaked_sample_results_.clear();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ResetCounter();
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ResetCounter() {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_result_index_ = 0;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual int GetSample(int width, int height) OVERRIDE {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (current_result_index_ >= prebaked_sample_results_.size()) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_result_index_ = 0;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (prebaked_sample_results_.empty()) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return prebaked_sample_results_[current_result_index_++];
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<int> prebaked_sample_results_;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t current_result_index_;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Return true if a color channel is approximately equal to an expected value.
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ChannelApproximatelyEqual(int expected, uint8_t channel) {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (abs(expected - static_cast<int>(channel)) <= 1);
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Compute minimal and maximal graylevel (or alphalevel) of the input |bitmap|.
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |bitmap| has to be allocated and configured to kA8_Config.
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void Calculate8bitBitmapMinMax(const SkBitmap& bitmap,
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               uint8_t* min_gl,
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               uint8_t* max_gl) {
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkAutoLockPixels bitmap_lock(bitmap);
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(bitmap.getPixels());
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(bitmap.config() == SkBitmap::kA8_Config);
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(min_gl);
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(max_gl);
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *min_gl = std::numeric_limits<uint8_t>::max();
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *max_gl = std::numeric_limits<uint8_t>::min();
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int y = 0; y < bitmap.height(); ++y) {
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    uint8_t* current_color = bitmap.getAddr8(0, y);
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int x = 0; x < bitmap.width(); ++x, ++current_color) {
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *min_gl = std::min(*min_gl, *current_color);
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *max_gl = std::max(*max_gl, *current_color);
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ColorAnalysisTest : public testing::Test {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanAllWhite) {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler test_sampler;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(0);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<base::RefCountedBytes> png(
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new base::RefCountedBytes(
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::vector<unsigned char>(
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              k1x1White,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              k1x1White + sizeof(k1x1White) / sizeof(unsigned char))));
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor color =
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(color, SK_ColorWHITE);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanIgnoreWhite) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler test_sampler;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(0);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(1);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(2);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<base::RefCountedBytes> png(
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     new base::RefCountedBytes(
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         std::vector<unsigned char>(
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueWhite,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueWhite + sizeof(k1x3BlueWhite) / sizeof(unsigned char))));
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor color =
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(color, SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ColorAnalysisTest, CalculatePNGKMeanPickMostCommon) {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockKMeanImageSampler test_sampler;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(0);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(1);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_sampler.AddSample(2);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<base::RefCountedBytes> png(
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     new base::RefCountedBytes(
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         std::vector<unsigned char>(
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueRed,
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             k1x3BlueRed + sizeof(k1x3BlueRed) / sizeof(unsigned char))));
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor color =
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      color_utils::CalculateKMeanColorOfPNG(png, 100, 600, &test_sampler);
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(color, SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, GridSampler) {
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  color_utils::GridSampler sampler;
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kWidth = 16;
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kHeight = 16;
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Sample starts at 1,1.
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 1 * kWidth, sampler.GetSample(kWidth, kHeight));
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 4 * kWidth, sampler.GetSample(kWidth, kHeight));
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 7 * kWidth, sampler.GetSample(kWidth, kHeight));
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1 + 10 * kWidth, sampler.GetSample(kWidth, kHeight));
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Step over by 3.
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 1 * kWidth, sampler.GetSample(kWidth, kHeight));
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 4 * kWidth, sampler.GetSample(kWidth, kHeight));
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 7 * kWidth, sampler.GetSample(kWidth, kHeight));
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(4 + 10 * kWidth, sampler.GetSample(kWidth, kHeight));
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, FindClosestColor) {
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Empty image returns input color.
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor color = FindClosestColor(NULL, 0, 0, SK_ColorRED);
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(SK_ColorRED, color);
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Single color image returns that color.
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap;
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.allocPixels();
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.eraseColor(SK_ColorWHITE);
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()),
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.width(),
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.height(),
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           SK_ColorRED);
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(SK_ColorWHITE, color);
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Write a black pixel into the image. A dark grey input pixel should match
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the black one in the image.
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint32_t* pixel = bitmap.getAddr32(0, 0);
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *pixel = SK_ColorBLACK;
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  color = FindClosestColor(static_cast<uint8_t*>(bitmap.getPixels()),
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.width(),
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           bitmap.height(),
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           SK_ColorDKGRAY);
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(SK_ColorBLACK, color);
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) {
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create a 16x16 bitmap to represent a favicon.
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap;
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.allocPixels();
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.eraseARGB(255, 100, 150, 200);
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor color = color_utils::CalculateKMeanColorOfBitmap(bitmap);
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255u, SkColorGetA(color));
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Color values are not exactly equal due to reversal of premultiplied alpha.
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Test a bitmap with an alpha channel.
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.eraseARGB(128, 100, 150, 200);
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  color = color_utils::CalculateKMeanColorOfBitmap(bitmap);
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Alpha channel should be ignored for dominant color calculation.
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255u, SkColorGetA(color));
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) {
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap;
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 200);
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(gfx::Matrix3F::Zeros(),
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            color_utils::ComputeColorCovariance(bitmap));
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.allocPixels();
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bitmap.eraseRGB(50, 150, 200);
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Matrix3F covariance = color_utils::ComputeColorCovariance(bitmap);
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The answer should be all zeros.
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(covariance == gfx::Matrix3F::Zeros());
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputeColorCovarianceWithCanvas) {
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Canvas canvas(gfx::Size(250, 200), ui::SCALE_FACTOR_100P, true);
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical stripes, with color bands set to 100
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // in overlapping stripes 150 pixels wide.
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 50, 200), SkColorSetRGB(100, 0, 0));
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(50, 0, 50, 200), SkColorSetRGB(100, 100, 0));
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 0, 50, 200), SkColorSetRGB(100, 100, 100));
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(150, 0, 50, 200), SkColorSetRGB(0, 100, 100));
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(200, 0, 50, 200), SkColorSetRGB(0, 0, 100));
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap bitmap =
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Matrix3F covariance = color_utils::ComputeColorCovariance(bitmap);
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Matrix3F expected_covariance = gfx::Matrix3F::Zeros();
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  expected_covariance.set(2400, 400, -1600,
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          400, 2400, 400,
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          -1600, 400, 2400);
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(expected_covariance, covariance);
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) {
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The test runs color reduction on a single-colot image, where results are
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bound to be uninteresting. This is an important edge case, though.
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source, result;
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source.setConfig(SkBitmap::kARGB_8888_Config, 300, 200);
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.setConfig(SkBitmap::kA8_Config, 300, 200);
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source.allocPixels();
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.allocPixels();
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source.eraseRGB(50, 150, 200);
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Vector3dF transform(1.0f, .5f, 0.1f);
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This transform, if not scaled, should result in GL=145.
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, false, &result));
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(145, min_gl);
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(145, max_gl);
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Now scan requesting rescale. Expect all 0.
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, true, &result));
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, max_gl);
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Test cliping to upper limit.
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  transform.set_z(1.1f);
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, false, &result));
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0xFF, min_gl);
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0xFF, max_gl);
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Test cliping to upper limit.
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  transform.Scale(-1.0f);
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, false, &result));
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0x0, min_gl);
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0x0, max_gl);
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) {
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check with images with multiple colors. This is really different only when
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the result is scaled.
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Canvas canvas(gfx::Size(300, 200), ui::SCALE_FACTOR_100P, true);
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical non-overlapping stripes 150 pixels wide.
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 150, 200), SkColorSetRGB(0, 0, 0));
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(150, 0, 150, 200), SkColorSetRGB(255, 255, 255));
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source =
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap result;
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.setConfig(SkBitmap::kA8_Config, 300, 200);
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.allocPixels();
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, true, &result));
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0)));
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199)));
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Reverse test.
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  transform.Scale(-1.0f);
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, true, &result));
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  min_gl = 0;
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  max_gl = 0;
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(0, 0)));
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) {
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check with images with multiple colors. This is really different only when
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the result is scaled.
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Canvas canvas(gfx::Size(300, 200), ui::SCALE_FACTOR_100P, true);
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical non-overlapping stripes 100 pixels wide.
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(100, 0, 0));
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(0, 255, 0));
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(0, 0, 128));
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source =
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap result;
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.setConfig(SkBitmap::kA8_Config, 300, 200);
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.allocPixels();
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, false, &result));
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(12, min_gl);
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(127, max_gl);
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(100U, SkColorGetA(result.getColor(0, 0)));
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ApplyColorReduction(
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source, transform, true, &result));
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(193U, SkColorGetA(result.getColor(0, 0)));
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputePrincipalComponentImageNotComputable) {
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source, result;
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source.setConfig(SkBitmap::kARGB_8888_Config, 300, 200);
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.setConfig(SkBitmap::kA8_Config, 300, 200);
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source.allocPixels();
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.allocPixels();
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source.eraseRGB(50, 150, 200);
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This computation should fail since all colors always vary together.
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_FALSE(color_utils::ComputePrincipalComponentImage(source, &result));
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ColorAnalysisTest, ComputePrincipalComponentImage) {
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Canvas canvas(gfx::Size(300, 200), ui::SCALE_FACTOR_100P, true);
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The image consists of vertical non-overlapping stripes 100 pixels wide.
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(10, 10, 10));
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(100, 100, 100));
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(255, 255, 255));
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap source =
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false);
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkBitmap result;
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.setConfig(SkBitmap::kA8_Config, 300, 200);
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.allocPixels();
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This computation should fail since all colors always vary together.
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(color_utils::ComputePrincipalComponentImage(source, &result));
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t min_gl = 0;
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint8_t max_gl = 0;
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(0, min_gl);
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(255, max_gl);
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0)));
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199)));
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(93U, SkColorGetA(result.getColor(150, 0)));
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
480