Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(347)

Side by Side Diff: ui/gfx/color_analysis.cc

Issue 289283004: Add ability to constrain dominant color selection to a HSL range. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix DCHECKS Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/color_analysis.h" 5 #include "ui/gfx/color_analysis.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "third_party/skia/include/core/SkBitmap.h" 13 #include "third_party/skia/include/core/SkBitmap.h"
14 #include "third_party/skia/include/core/SkUnPreMultiply.h" 14 #include "third_party/skia/include/core/SkUnPreMultiply.h"
15 #include "ui/gfx/codec/png_codec.h" 15 #include "ui/gfx/codec/png_codec.h"
16 #include "ui/gfx/color_utils.h"
16 17
18 namespace color_utils {
17 namespace { 19 namespace {
18 20
19 // RGBA KMean Constants 21 // RGBA KMean Constants
20 const uint32_t kNumberOfClusters = 4; 22 const uint32_t kNumberOfClusters = 4;
21 const int kNumberOfIterations = 50; 23 const int kNumberOfIterations = 50;
22 const uint32_t kMaxBrightness = 665; 24
23 const uint32_t kMinDarkness = 100; 25 const HSL kDefaultLowerHSLBound = {-1, -1, 0.15};
26 const HSL kDefaultUpperHSLBound = {-1, -1, 0.85};
24 27
25 // Background Color Modification Constants 28 // Background Color Modification Constants
26 const SkColor kDefaultBgColor = SK_ColorWHITE; 29 const SkColor kDefaultBgColor = SK_ColorWHITE;
27 30
28 // Support class to hold information about each cluster of pixel data in 31 // Support class to hold information about each cluster of pixel data in
29 // the KMean algorithm. While this class does not contain all of the points 32 // the KMean algorithm. While this class does not contain all of the points
30 // that exist in the cluster, it keeps track of the aggregate sum so it can 33 // that exist in the cluster, it keeps track of the aggregate sum so it can
31 // compute the new center appropriately. 34 // compute the new center appropriately.
32 class KMeanCluster { 35 class KMeanCluster {
33 public: 36 public:
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 SkAutoLockPixels auto_lock(bitmap); 135 SkAutoLockPixels auto_lock(bitmap);
133 uint32_t* in = static_cast<uint32_t*>(bitmap.getPixels()); 136 uint32_t* in = static_cast<uint32_t*>(bitmap.getPixels());
134 uint32_t* out = buffer; 137 uint32_t* out = buffer;
135 int pixel_count = std::min(bitmap.width() * bitmap.height(), buffer_size); 138 int pixel_count = std::min(bitmap.width() * bitmap.height(), buffer_size);
136 for (int i = 0; i < pixel_count; ++i) 139 for (int i = 0; i < pixel_count; ++i)
137 *out++ = SkUnPreMultiply::PMColorToColor(*in++); 140 *out++ = SkUnPreMultiply::PMColorToColor(*in++);
138 } 141 }
139 142
140 } // namespace 143 } // namespace
141 144
142 namespace color_utils {
143
144 KMeanImageSampler::KMeanImageSampler() { 145 KMeanImageSampler::KMeanImageSampler() {
145 } 146 }
146 147
147 KMeanImageSampler::~KMeanImageSampler() { 148 KMeanImageSampler::~KMeanImageSampler() {
148 } 149 }
149 150
150 GridSampler::GridSampler() : calls_(0) { 151 GridSampler::GridSampler() : calls_(0) {
151 } 152 }
152 153
153 GridSampler::~GridSampler() { 154 GridSampler::~GridSampler() {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 } 208 }
208 return best_color; 209 return best_color;
209 } 210 }
210 211
211 // For a 16x16 icon on an Intel Core i5 this function takes approximately 212 // For a 16x16 icon on an Intel Core i5 this function takes approximately
212 // 0.5 ms to run. 213 // 0.5 ms to run.
213 // TODO(port): This code assumes the CPU architecture is little-endian. 214 // TODO(port): This code assumes the CPU architecture is little-endian.
214 SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, 215 SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data,
215 int img_width, 216 int img_width,
216 int img_height, 217 int img_height,
217 uint32_t darkness_limit, 218 const HSL& lower_bound,
218 uint32_t brightness_limit, 219 const HSL& upper_bound,
219 KMeanImageSampler* sampler) { 220 KMeanImageSampler* sampler) {
220 SkColor color = kDefaultBgColor; 221 SkColor color = kDefaultBgColor;
221 if (img_width > 0 && img_height > 0) { 222 if (img_width > 0 && img_height > 0) {
222 std::vector<KMeanCluster> clusters; 223 std::vector<KMeanCluster> clusters;
223 clusters.resize(kNumberOfClusters, KMeanCluster()); 224 clusters.resize(kNumberOfClusters, KMeanCluster());
224 225
225 // Pick a starting point for each cluster 226 // Pick a starting point for each cluster
226 std::vector<KMeanCluster>::iterator cluster = clusters.begin(); 227 std::vector<KMeanCluster>::iterator cluster = clusters.begin();
227 while (cluster != clusters.end()) { 228 while (cluster != clusters.end()) {
228 // Try up to 10 times to find a unique color. If no unique color can be 229 // Try up to 10 times to find a unique color. If no unique color can be
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 // color is. 325 // color is.
325 std::sort(clusters.begin(), clusters.end(), 326 std::sort(clusters.begin(), clusters.end(),
326 KMeanCluster::SortKMeanClusterByWeight); 327 KMeanCluster::SortKMeanClusterByWeight);
327 328
328 // Loop through the clusters to figure out which cluster has an appropriate 329 // Loop through the clusters to figure out which cluster has an appropriate
329 // color. Skip any that are too bright/dark and go in order of weight. 330 // color. Skip any that are too bright/dark and go in order of weight.
330 for (std::vector<KMeanCluster>::iterator cluster = clusters.begin(); 331 for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
331 cluster != clusters.end(); ++cluster) { 332 cluster != clusters.end(); ++cluster) {
332 uint8_t r, g, b; 333 uint8_t r, g, b;
333 cluster->GetCentroid(&r, &g, &b); 334 cluster->GetCentroid(&r, &g, &b);
334 // Sum the RGB components to determine if the color is too bright or too
335 // dark.
336 // TODO (dtrainor): Look into using HSV here instead. This approximation
337 // might be fine though.
338 uint32_t summed_color = r + g + b;
339 335
340 if (summed_color < brightness_limit && summed_color > darkness_limit) { 336 SkColor current_color = SkColorSetARGB(SK_AlphaOPAQUE, r, g, b);
337 HSL hsl;
338 SkColorToHSL(current_color, &hsl);
339 if (IsWithinHSLRange(hsl, lower_bound, upper_bound)) {
341 // If we found a valid color just set it and break. We don't want to 340 // If we found a valid color just set it and break. We don't want to
342 // check the other ones. 341 // check the other ones.
343 color = SkColorSetARGB(0xFF, r, g, b); 342 color = current_color;
344 break; 343 break;
345 } else if (cluster == clusters.begin()) { 344 } else if (cluster == clusters.begin()) {
346 // We haven't found a valid color, but we are at the first color so 345 // We haven't found a valid color, but we are at the first color so
347 // set the color anyway to make sure we at least have a value here. 346 // set the color anyway to make sure we at least have a value here.
348 color = SkColorSetARGB(0xFF, r, g, b); 347 color = current_color;
349 } 348 }
350 } 349 }
351 } 350 }
352 351
353 // Find a color that actually appears in the image (the K-mean cluster center 352 // Find a color that actually appears in the image (the K-mean cluster center
354 // will not usually be a color that appears in the image). 353 // will not usually be a color that appears in the image).
355 return FindClosestColor(decoded_data, img_width, img_height, color); 354 return FindClosestColor(decoded_data, img_width, img_height, color);
356 } 355 }
357 356
358 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png, 357 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png,
359 uint32_t darkness_limit, 358 const HSL& lower_bound,
360 uint32_t brightness_limit, 359 const HSL& upper_bound,
361 KMeanImageSampler* sampler) { 360 KMeanImageSampler* sampler) {
362 int img_width = 0; 361 int img_width = 0;
363 int img_height = 0; 362 int img_height = 0;
364 std::vector<uint8_t> decoded_data; 363 std::vector<uint8_t> decoded_data;
365 SkColor color = kDefaultBgColor; 364 SkColor color = kDefaultBgColor;
366 365
367 if (png.get() && 366 if (png.get() && png->size() &&
368 png->size() &&
369 gfx::PNGCodec::Decode(png->front(), 367 gfx::PNGCodec::Decode(png->front(),
370 png->size(), 368 png->size(),
371 gfx::PNGCodec::FORMAT_BGRA, 369 gfx::PNGCodec::FORMAT_BGRA,
372 &decoded_data, 370 &decoded_data,
373 &img_width, 371 &img_width,
374 &img_height)) { 372 &img_height)) {
375 return CalculateKMeanColorOfBuffer(&decoded_data[0], 373 return CalculateKMeanColorOfBuffer(&decoded_data[0],
376 img_width, 374 img_width,
377 img_height, 375 img_height,
378 darkness_limit, 376 lower_bound,
379 brightness_limit, 377 upper_bound,
380 sampler); 378 sampler);
381 } 379 }
382 return color; 380 return color;
383 } 381 }
384 382
385 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png) { 383 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png) {
386 GridSampler sampler; 384 GridSampler sampler;
387 return CalculateKMeanColorOfPNG(png, kMinDarkness, kMaxBrightness, &sampler); 385 return CalculateKMeanColorOfPNG(
386 png, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler);
388 } 387 }
389 388
390 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap, 389 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap,
391 uint32_t darkness_limit, 390 const HSL& lower_bound,
392 uint32_t brightness_limit, 391 const HSL& upper_bound,
393 KMeanImageSampler* sampler) { 392 KMeanImageSampler* sampler) {
394 // SkBitmap uses pre-multiplied alpha but the KMean clustering function 393 // SkBitmap uses pre-multiplied alpha but the KMean clustering function
395 // above uses non-pre-multiplied alpha. Transform the bitmap before we 394 // above uses non-pre-multiplied alpha. Transform the bitmap before we
396 // analyze it because the function reads each pixel multiple times. 395 // analyze it because the function reads each pixel multiple times.
397 int pixel_count = bitmap.width() * bitmap.height(); 396 int pixel_count = bitmap.width() * bitmap.height();
398 scoped_ptr<uint32_t[]> image(new uint32_t[pixel_count]); 397 scoped_ptr<uint32_t[]> image(new uint32_t[pixel_count]);
399 UnPreMultiply(bitmap, image.get(), pixel_count); 398 UnPreMultiply(bitmap, image.get(), pixel_count);
400 399
401 return CalculateKMeanColorOfBuffer(reinterpret_cast<uint8_t*>(image.get()), 400 return CalculateKMeanColorOfBuffer(reinterpret_cast<uint8_t*>(image.get()),
402 bitmap.width(), 401 bitmap.width(),
403 bitmap.height(), 402 bitmap.height(),
404 darkness_limit, 403 lower_bound,
405 brightness_limit, 404 upper_bound,
406 sampler); 405 sampler);
407 } 406 }
408 407
409 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) { 408 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) {
410 GridSampler sampler; 409 GridSampler sampler;
411 return CalculateKMeanColorOfBitmap( 410 return CalculateKMeanColorOfBitmap(
412 bitmap, kMinDarkness, kMaxBrightness, &sampler); 411 bitmap, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler);
413 } 412 }
414 413
415 gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) { 414 gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) {
416 // First need basic stats to normalize each channel separately. 415 // First need basic stats to normalize each channel separately.
417 SkAutoLockPixels bitmap_lock(bitmap); 416 SkAutoLockPixels bitmap_lock(bitmap);
418 gfx::Matrix3F covariance = gfx::Matrix3F::Zeros(); 417 gfx::Matrix3F covariance = gfx::Matrix3F::Zeros();
419 if (!bitmap.getPixels()) 418 if (!bitmap.getPixels())
420 return covariance; 419 return covariance;
421 420
422 // Assume ARGB_8888 format. 421 // Assume ARGB_8888 format.
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
565 gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap); 564 gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap);
566 gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros(); 565 gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros();
567 gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors); 566 gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors);
568 gfx::Vector3dF principal = eigenvectors.get_column(0); 567 gfx::Vector3dF principal = eigenvectors.get_column(0);
569 if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF()) 568 if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF())
570 return false; // This may happen for some edge cases. 569 return false; // This may happen for some edge cases.
571 return ApplyColorReduction(source_bitmap, principal, true, target_bitmap); 570 return ApplyColorReduction(source_bitmap, principal, true, target_bitmap);
572 } 571 }
573 572
574 } // color_utils 573 } // color_utils
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698