This source file includes following definitions.
- Reset
- SetCentroid
- GetCentroid
- IsAtCentroid
- RecomputeCentroid
- AddPoint
- GetDistanceSqr
- CompareCentroidWithAggregate
- GetWeight
- SortKMeanClusterByWeight
- UnPreMultiply
- GetSample
- FindClosestColor
- CalculateKMeanColorOfBuffer
- CalculateKMeanColorOfPNG
- CalculateKMeanColorOfBitmap
- ComputeColorCovariance
- ApplyColorReduction
- ComputePrincipalComponentImage
#include "ui/gfx/color_analysis.h"
#include <algorithm>
#include <limits>
#include <vector>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkUnPreMultiply.h"
#include "ui/gfx/codec/png_codec.h"
namespace {
const uint32_t kNumberOfClusters = 4;
const int kNumberOfIterations = 50;
const uint32_t kMaxBrightness = 665;
const uint32_t kMinDarkness = 100;
const SkColor kDefaultBgColor = SK_ColorWHITE;
class KMeanCluster {
 public:
  KMeanCluster() {
    Reset();
  }
  void Reset() {
    centroid[0] = centroid[1] = centroid[2] = 0;
    aggregate[0] = aggregate[1] = aggregate[2] = 0;
    counter = 0;
    weight = 0;
  }
  inline void SetCentroid(uint8_t r, uint8_t g, uint8_t b) {
    centroid[0] = r;
    centroid[1] = g;
    centroid[2] = b;
  }
  inline void GetCentroid(uint8_t* r, uint8_t* g, uint8_t* b) {
    *r = centroid[0];
    *g = centroid[1];
    *b = centroid[2];
  }
  inline bool IsAtCentroid(uint8_t r, uint8_t g, uint8_t b) {
    return r == centroid[0] && g == centroid[1] && b == centroid[2];
  }
  
  
  
  
  inline void RecomputeCentroid() {
    if (counter > 0) {
      centroid[0] = aggregate[0] / counter;
      centroid[1] = aggregate[1] / counter;
      centroid[2] = aggregate[2] / counter;
      aggregate[0] = aggregate[1] = aggregate[2] = 0;
      weight = counter;
      counter = 0;
    }
  }
  inline void AddPoint(uint8_t r, uint8_t g, uint8_t b) {
    aggregate[0] += r;
    aggregate[1] += g;
    aggregate[2] += b;
    ++counter;
  }
  
  
  inline uint32_t GetDistanceSqr(uint8_t r, uint8_t g, uint8_t b) {
    return (r - centroid[0]) * (r - centroid[0]) +
           (g - centroid[1]) * (g - centroid[1]) +
           (b - centroid[2]) * (b - centroid[2]);
  }
  
  
  
  
  inline bool CompareCentroidWithAggregate() {
    if (counter == 0)
      return false;
    return aggregate[0] / counter == centroid[0] &&
           aggregate[1] / counter == centroid[1] &&
           aggregate[2] / counter == centroid[2];
  }
  
  
  inline uint32_t GetWeight() const {
    return weight;
  }
  static bool SortKMeanClusterByWeight(const KMeanCluster& a,
                                       const KMeanCluster& b) {
    return a.GetWeight() > b.GetWeight();
  }
 private:
  uint8_t centroid[3];
  
  
  uint32_t aggregate[3];
  uint32_t counter;
  
  
  uint32_t weight;
};
void UnPreMultiply(const SkBitmap& bitmap, uint32_t* buffer, int buffer_size) {
  SkAutoLockPixels auto_lock(bitmap);
  uint32_t* in = static_cast<uint32_t*>(bitmap.getPixels());
  uint32_t* out = buffer;
  int pixel_count = std::min(bitmap.width() * bitmap.height(), buffer_size);
  for (int i = 0; i < pixel_count; ++i)
    *out++ = SkUnPreMultiply::PMColorToColor(*in++);
}
} 
namespace color_utils {
KMeanImageSampler::KMeanImageSampler() {
}
KMeanImageSampler::~KMeanImageSampler() {
}
GridSampler::GridSampler() : calls_(0) {
}
GridSampler::~GridSampler() {
}
int GridSampler::GetSample(int width, int height) {
  
  
  
  
  
  
  
  
  
  
  
  
  const int kPadX = 1;
  const int kPadY = 1;
  int x = kPadX +
      (calls_ / kNumberOfClusters) * ((width - 2 * kPadX) / kNumberOfClusters);
  int y = kPadY +
      (calls_ % kNumberOfClusters) * ((height - 2 * kPadY) / kNumberOfClusters);
  int index = x + (y * width);
  ++calls_;
  return index % (width * height);
}
SkColor FindClosestColor(const uint8_t* image,
                         int width,
                         int height,
                         SkColor color) {
  uint8_t in_r = SkColorGetR(color);
  uint8_t in_g = SkColorGetG(color);
  uint8_t in_b = SkColorGetB(color);
  
  int best_distance_squared = kint32max;
  SkColor best_color = color;
  const uint8_t* byte = image;
  for (int i = 0; i < width * height; ++i) {
    uint8_t b = *(byte++);
    uint8_t g = *(byte++);
    uint8_t r = *(byte++);
    uint8_t a = *(byte++);
    
    if (a == 0)
      continue;
    int distance_squared =
        (in_b - b) * (in_b - b) +
        (in_g - g) * (in_g - g) +
        (in_r - r) * (in_r - r);
    if (distance_squared < best_distance_squared) {
      best_distance_squared = distance_squared;
      best_color = SkColorSetRGB(r, g, b);
    }
  }
  return best_color;
}
SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data,
                                    int img_width,
                                    int img_height,
                                    uint32_t darkness_limit,
                                    uint32_t brightness_limit,
                                    KMeanImageSampler* sampler) {
  SkColor color = kDefaultBgColor;
  if (img_width > 0 && img_height > 0) {
    std::vector<KMeanCluster> clusters;
    clusters.resize(kNumberOfClusters, KMeanCluster());
    
    std::vector<KMeanCluster>::iterator cluster = clusters.begin();
    while (cluster != clusters.end()) {
      
      
      bool color_unique = false;
      for (int i = 0; i < 10; ++i) {
        int pixel_pos = sampler->GetSample(img_width, img_height) %
            (img_width * img_height);
        uint8_t b = decoded_data[pixel_pos * 4];
        uint8_t g = decoded_data[pixel_pos * 4 + 1];
        uint8_t r = decoded_data[pixel_pos * 4 + 2];
        uint8_t a = decoded_data[pixel_pos * 4 + 3];
        
        
        if (a == 0)
          continue;
        
        
        color_unique = true;
        for (std::vector<KMeanCluster>::iterator
            cluster_check = clusters.begin();
            cluster_check != cluster; ++cluster_check) {
          if (cluster_check->IsAtCentroid(r, g, b)) {
            color_unique = false;
            break;
          }
        }
        
        
        if (color_unique) {
          cluster->SetCentroid(r, g, b);
          break;
        }
      }
      
      if (!color_unique) {
        cluster = clusters.erase(cluster);
      } else {
        
        
        
        ++cluster;
      }
    }
    
    if (clusters.empty())
      return color;
    bool convergence = false;
    for (int iteration = 0;
        iteration < kNumberOfIterations && !convergence;
        ++iteration) {
      
      uint8_t* pixel = decoded_data;
      uint8_t* decoded_data_end = decoded_data + (img_width * img_height * 4);
      while (pixel < decoded_data_end) {
        uint8_t b = *(pixel++);
        uint8_t g = *(pixel++);
        uint8_t r = *(pixel++);
        uint8_t a = *(pixel++);
        
        if (a == 0)
          continue;
        uint32_t distance_sqr_to_closest_cluster = UINT_MAX;
        std::vector<KMeanCluster>::iterator closest_cluster = clusters.begin();
        
        for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
            cluster != clusters.end(); ++cluster) {
          uint32_t distance_sqr = cluster->GetDistanceSqr(r, g, b);
          if (distance_sqr < distance_sqr_to_closest_cluster) {
            distance_sqr_to_closest_cluster = distance_sqr;
            closest_cluster = cluster;
          }
        }
        closest_cluster->AddPoint(r, g, b);
      }
      
      convergence = true;
      for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
          cluster != clusters.end(); ++cluster) {
        convergence &= cluster->CompareCentroidWithAggregate();
        cluster->RecomputeCentroid();
      }
    }
    
    
    std::sort(clusters.begin(), clusters.end(),
              KMeanCluster::SortKMeanClusterByWeight);
    
    
    for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
        cluster != clusters.end(); ++cluster) {
      uint8_t r, g, b;
      cluster->GetCentroid(&r, &g, &b);
      
      
      
      
      uint32_t summed_color = r + g + b;
      if (summed_color < brightness_limit && summed_color > darkness_limit) {
        
        
        color = SkColorSetARGB(0xFF, r, g, b);
        break;
      } else if (cluster == clusters.begin()) {
        
        
        color = SkColorSetARGB(0xFF, r, g, b);
      }
    }
  }
  
  
  return FindClosestColor(decoded_data, img_width, img_height, color);
}
SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png,
                                 uint32_t darkness_limit,
                                 uint32_t brightness_limit,
                                 KMeanImageSampler* sampler) {
  int img_width = 0;
  int img_height = 0;
  std::vector<uint8_t> decoded_data;
  SkColor color = kDefaultBgColor;
  if (png.get() &&
      png->size() &&
      gfx::PNGCodec::Decode(png->front(),
                            png->size(),
                            gfx::PNGCodec::FORMAT_BGRA,
                            &decoded_data,
                            &img_width,
                            &img_height)) {
    return CalculateKMeanColorOfBuffer(&decoded_data[0],
                                       img_width,
                                       img_height,
                                       darkness_limit,
                                       brightness_limit,
                                       sampler);
  }
  return color;
}
SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) {
  
  
  
  int pixel_count = bitmap.width() * bitmap.height();
  scoped_ptr<uint32_t[]> image(new uint32_t[pixel_count]);
  UnPreMultiply(bitmap, image.get(), pixel_count);
  GridSampler sampler;
  SkColor color = CalculateKMeanColorOfBuffer(
      reinterpret_cast<uint8_t*>(image.get()),
      bitmap.width(),
      bitmap.height(),
      kMinDarkness,
      kMaxBrightness,
      &sampler);
  return color;
}
gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) {
  
  SkAutoLockPixels bitmap_lock(bitmap);
  gfx::Matrix3F covariance = gfx::Matrix3F::Zeros();
  if (!bitmap.getPixels())
    return covariance;
  
  DCHECK(bitmap.colorType() == kPMColor_SkColorType);
  int64_t r_sum = 0;
  int64_t g_sum = 0;
  int64_t b_sum = 0;
  int64_t rr_sum = 0;
  int64_t gg_sum = 0;
  int64_t bb_sum = 0;
  int64_t rg_sum = 0;
  int64_t rb_sum = 0;
  int64_t gb_sum = 0;
  for (int y = 0; y < bitmap.height(); ++y) {
    SkPMColor* current_color = static_cast<uint32_t*>(bitmap.getAddr32(0, y));
    for (int x = 0; x < bitmap.width(); ++x, ++current_color) {
      SkColor c = SkUnPreMultiply::PMColorToColor(*current_color);
      SkColor r = SkColorGetR(c);
      SkColor g = SkColorGetG(c);
      SkColor b = SkColorGetB(c);
      r_sum += r;
      g_sum += g;
      b_sum += b;
      rr_sum += r * r;
      gg_sum += g * g;
      bb_sum += b * b;
      rg_sum += r * g;
      rb_sum += r * b;
      gb_sum += g * b;
    }
  }
  
  
  
  
  int pixel_n = bitmap.width() * bitmap.height();
  covariance.set(
      (static_cast<double>(rr_sum) / pixel_n -
       static_cast<double>(r_sum * r_sum) / pixel_n / pixel_n),
      (static_cast<double>(rg_sum) / pixel_n -
       static_cast<double>(r_sum * g_sum) / pixel_n / pixel_n),
      (static_cast<double>(rb_sum) / pixel_n -
       static_cast<double>(r_sum * b_sum) / pixel_n / pixel_n),
      (static_cast<double>(rg_sum) / pixel_n -
       static_cast<double>(r_sum * g_sum) / pixel_n / pixel_n),
      (static_cast<double>(gg_sum) / pixel_n -
       static_cast<double>(g_sum * g_sum) / pixel_n / pixel_n),
      (static_cast<double>(gb_sum) / pixel_n -
       static_cast<double>(g_sum * b_sum) / pixel_n / pixel_n),
      (static_cast<double>(rb_sum) / pixel_n -
       static_cast<double>(r_sum * b_sum) / pixel_n / pixel_n),
      (static_cast<double>(gb_sum) / pixel_n -
       static_cast<double>(g_sum * b_sum) / pixel_n / pixel_n),
      (static_cast<double>(bb_sum) / pixel_n -
       static_cast<double>(b_sum * b_sum) / pixel_n / pixel_n));
  return covariance;
}
bool ApplyColorReduction(const SkBitmap& source_bitmap,
                         const gfx::Vector3dF& color_transform,
                         bool fit_to_range,
                         SkBitmap* target_bitmap) {
  DCHECK(target_bitmap);
  SkAutoLockPixels source_lock(source_bitmap);
  SkAutoLockPixels target_lock(*target_bitmap);
  DCHECK(source_bitmap.getPixels());
  DCHECK(target_bitmap->getPixels());
  DCHECK_EQ(kPMColor_SkColorType, source_bitmap.colorType());
  DCHECK_EQ(kAlpha_8_SkColorType, target_bitmap->colorType());
  DCHECK_EQ(source_bitmap.height(), target_bitmap->height());
  DCHECK_EQ(source_bitmap.width(), target_bitmap->width());
  DCHECK(!source_bitmap.empty());
  
  
  
  float t0 = 0.0;
  float tr = color_transform.x();
  float tg = color_transform.y();
  float tb = color_transform.z();
  if (fit_to_range) {
    
    
    float max_val = std::numeric_limits<float>::min();
    float min_val = std::numeric_limits<float>::max();
    for (int y = 0; y < source_bitmap.height(); ++y) {
      const SkPMColor* source_color_row = static_cast<SkPMColor*>(
          source_bitmap.getAddr32(0, y));
      for (int x = 0; x < source_bitmap.width(); ++x) {
        SkColor c = SkUnPreMultiply::PMColorToColor(source_color_row[x]);
        float r = SkColorGetR(c);
        float g = SkColorGetG(c);
        float b = SkColorGetB(c);
        float gray_level = tr * r + tg * g + tb * b;
        max_val = std::max(max_val, gray_level);
        min_val = std::min(min_val, gray_level);
      }
    }
    
    float scale = 0.0;
    t0 = -min_val;
    if (max_val > min_val)
      scale = 255.0 / (max_val - min_val);
    t0 *= scale;
    tr *= scale;
    tg *= scale;
    tb *= scale;
  }
  for (int y = 0; y < source_bitmap.height(); ++y) {
    const SkPMColor* source_color_row = static_cast<SkPMColor*>(
        source_bitmap.getAddr32(0, y));
    uint8_t* target_color_row = target_bitmap->getAddr8(0, y);
    for (int x = 0; x < source_bitmap.width(); ++x) {
      SkColor c = SkUnPreMultiply::PMColorToColor(source_color_row[x]);
      float r = SkColorGetR(c);
      float g = SkColorGetG(c);
      float b = SkColorGetB(c);
      float gl = t0 + tr * r + tg * g + tb * b;
      if (gl < 0)
        gl = 0;
      if (gl > 0xFF)
        gl = 0xFF;
      target_color_row[x] = static_cast<uint8_t>(gl);
    }
  }
  return true;
}
bool ComputePrincipalComponentImage(const SkBitmap& source_bitmap,
                                    SkBitmap* target_bitmap) {
  if (!target_bitmap) {
    NOTREACHED();
    return false;
  }
  gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap);
  gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros();
  gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors);
  gfx::Vector3dF principal = eigenvectors.get_column(0);
  if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF())
    return false;  
  return ApplyColorReduction(source_bitmap, principal, true, target_bitmap);
}
}