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

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: rebase, fix nits Created 6 years, 7 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
17 namespace { 18 namespace {
18 19
19 // RGBA KMean Constants 20 // RGBA KMean Constants
20 const uint32_t kNumberOfClusters = 4; 21 const uint32_t kNumberOfClusters = 4;
21 const int kNumberOfIterations = 50; 22 const int kNumberOfIterations = 50;
22 const uint32_t kMaxBrightness = 665; 23
23 const uint32_t kMinDarkness = 100; 24 const color_utils::HSL kDefaultLowerHSLBound = {-1, -1, 0.15};
25 const color_utils::HSL kDefaultUpperHSLBound = {-1, -1, 0.85};
24 26
25 // Background Color Modification Constants 27 // Background Color Modification Constants
26 const SkColor kDefaultBgColor = SK_ColorWHITE; 28 const SkColor kDefaultBgColor = SK_ColorWHITE;
27 29
28 // Support class to hold information about each cluster of pixel data in 30 // 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 31 // 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 32 // that exist in the cluster, it keeps track of the aggregate sum so it can
31 // compute the new center appropriately. 33 // compute the new center appropriately.
32 class KMeanCluster { 34 class KMeanCluster {
33 public: 35 public:
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 } 209 }
208 return best_color; 210 return best_color;
209 } 211 }
210 212
211 // For a 16x16 icon on an Intel Core i5 this function takes approximately 213 // For a 16x16 icon on an Intel Core i5 this function takes approximately
212 // 0.5 ms to run. 214 // 0.5 ms to run.
213 // TODO(port): This code assumes the CPU architecture is little-endian. 215 // TODO(port): This code assumes the CPU architecture is little-endian.
214 SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, 216 SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data,
215 int img_width, 217 int img_width,
216 int img_height, 218 int img_height,
217 uint32_t darkness_limit, 219 const HSL& lower_bound,
218 uint32_t brightness_limit, 220 const HSL& upper_bound,
219 KMeanImageSampler* sampler) { 221 KMeanImageSampler* sampler) {
220 SkColor color = kDefaultBgColor; 222 SkColor color = kDefaultBgColor;
221 if (img_width > 0 && img_height > 0) { 223 if (img_width > 0 && img_height > 0) {
222 std::vector<KMeanCluster> clusters; 224 std::vector<KMeanCluster> clusters;
223 clusters.resize(kNumberOfClusters, KMeanCluster()); 225 clusters.resize(kNumberOfClusters, KMeanCluster());
224 226
225 // Pick a starting point for each cluster 227 // Pick a starting point for each cluster
226 std::vector<KMeanCluster>::iterator cluster = clusters.begin(); 228 std::vector<KMeanCluster>::iterator cluster = clusters.begin();
227 while (cluster != clusters.end()) { 229 while (cluster != clusters.end()) {
228 // Try up to 10 times to find a unique color. If no unique color can be 230 // 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. 326 // color is.
325 std::sort(clusters.begin(), clusters.end(), 327 std::sort(clusters.begin(), clusters.end(),
326 KMeanCluster::SortKMeanClusterByWeight); 328 KMeanCluster::SortKMeanClusterByWeight);
327 329
328 // Loop through the clusters to figure out which cluster has an appropriate 330 // 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. 331 // color. Skip any that are too bright/dark and go in order of weight.
330 for (std::vector<KMeanCluster>::iterator cluster = clusters.begin(); 332 for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
331 cluster != clusters.end(); ++cluster) { 333 cluster != clusters.end(); ++cluster) {
332 uint8_t r, g, b; 334 uint8_t r, g, b;
333 cluster->GetCentroid(&r, &g, &b); 335 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 336
340 if (summed_color < brightness_limit && summed_color > darkness_limit) { 337 SkColor current_color = SkColorSetARGB(SK_AlphaOPAQUE, r, g, b);
338 HSL hsl;
339 SkColorToHSL(current_color, &hsl);
340 if (color_utils::IsWithinHSLRange(hsl, lower_bound, upper_bound)) {
341 // If we found a valid color just set it and break. We don't want to 341 // If we found a valid color just set it and break. We don't want to
342 // check the other ones. 342 // check the other ones.
343 color = SkColorSetARGB(0xFF, r, g, b); 343 color = current_color;
344 break; 344 break;
345 } else if (cluster == clusters.begin()) { 345 } else if (cluster == clusters.begin()) {
346 // We haven't found a valid color, but we are at the first color so 346 // 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. 347 // set the color anyway to make sure we at least have a value here.
348 color = SkColorSetARGB(0xFF, r, g, b); 348 color = current_color;
349 } 349 }
350 } 350 }
351 } 351 }
352 352
353 // Find a color that actually appears in the image (the K-mean cluster center 353 // 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). 354 // will not usually be a color that appears in the image).
355 return FindClosestColor(decoded_data, img_width, img_height, color); 355 return FindClosestColor(decoded_data, img_width, img_height, color);
356 } 356 }
357 357
358 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png, 358 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png,
359 uint32_t darkness_limit, 359 const HSL& lower_bound,
360 uint32_t brightness_limit, 360 const HSL& upper_bound,
361 KMeanImageSampler* sampler) { 361 KMeanImageSampler* sampler) {
362 int img_width = 0; 362 int img_width = 0;
363 int img_height = 0; 363 int img_height = 0;
364 std::vector<uint8_t> decoded_data; 364 std::vector<uint8_t> decoded_data;
365 SkColor color = kDefaultBgColor; 365 SkColor color = kDefaultBgColor;
366 366
367 if (png.get() && 367 if (png.get() && png->size() &&
368 png->size() &&
369 gfx::PNGCodec::Decode(png->front(), 368 gfx::PNGCodec::Decode(png->front(),
370 png->size(), 369 png->size(),
371 gfx::PNGCodec::FORMAT_BGRA, 370 gfx::PNGCodec::FORMAT_BGRA,
372 &decoded_data, 371 &decoded_data,
373 &img_width, 372 &img_width,
374 &img_height)) { 373 &img_height)) {
375 return CalculateKMeanColorOfBuffer(&decoded_data[0], 374 return CalculateKMeanColorOfBuffer(&decoded_data[0],
376 img_width, 375 img_width,
377 img_height, 376 img_height,
378 darkness_limit, 377 lower_bound,
379 brightness_limit, 378 upper_bound,
380 sampler); 379 sampler);
381 } 380 }
382 return color; 381 return color;
383 } 382 }
384 383
385 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png) { 384 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png) {
386 GridSampler sampler; 385 GridSampler sampler;
387 return CalculateKMeanColorOfPNG(png, kMinDarkness, kMaxBrightness, &sampler); 386 return CalculateKMeanColorOfPNG(
387 png, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler);
388 } 388 }
389 389
390 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap, 390 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap,
391 uint32_t darkness_limit, 391 const HSL& lower_bound,
392 uint32_t brightness_limit, 392 const HSL& upper_bound,
393 KMeanImageSampler* sampler) { 393 KMeanImageSampler* sampler) {
394 // SkBitmap uses pre-multiplied alpha but the KMean clustering function 394 // SkBitmap uses pre-multiplied alpha but the KMean clustering function
395 // above uses non-pre-multiplied alpha. Transform the bitmap before we 395 // above uses non-pre-multiplied alpha. Transform the bitmap before we
396 // analyze it because the function reads each pixel multiple times. 396 // analyze it because the function reads each pixel multiple times.
397 int pixel_count = bitmap.width() * bitmap.height(); 397 int pixel_count = bitmap.width() * bitmap.height();
398 scoped_ptr<uint32_t[]> image(new uint32_t[pixel_count]); 398 scoped_ptr<uint32_t[]> image(new uint32_t[pixel_count]);
399 UnPreMultiply(bitmap, image.get(), pixel_count); 399 UnPreMultiply(bitmap, image.get(), pixel_count);
400 400
401 SkColor color = 401 SkColor color =
402 CalculateKMeanColorOfBuffer(reinterpret_cast<uint8_t*>(image.get()), 402 CalculateKMeanColorOfBuffer(reinterpret_cast<uint8_t*>(image.get()),
403 bitmap.width(), 403 bitmap.width(),
404 bitmap.height(), 404 bitmap.height(),
405 darkness_limit, 405 lower_bound,
406 brightness_limit, 406 upper_bound,
407 sampler); 407 sampler);
408 return color; 408 return color;
409 } 409 }
410 410
411 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) { 411 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) {
412 GridSampler sampler; 412 GridSampler sampler;
413 return CalculateKMeanColorOfBitmap( 413 return CalculateKMeanColorOfBitmap(
414 bitmap, kMinDarkness, kMaxBrightness, &sampler); 414 bitmap, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler);
415 } 415 }
416 416
417 gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) { 417 gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) {
418 // First need basic stats to normalize each channel separately. 418 // First need basic stats to normalize each channel separately.
419 SkAutoLockPixels bitmap_lock(bitmap); 419 SkAutoLockPixels bitmap_lock(bitmap);
420 gfx::Matrix3F covariance = gfx::Matrix3F::Zeros(); 420 gfx::Matrix3F covariance = gfx::Matrix3F::Zeros();
421 if (!bitmap.getPixels()) 421 if (!bitmap.getPixels())
422 return covariance; 422 return covariance;
423 423
424 // Assume ARGB_8888 format. 424 // Assume ARGB_8888 format.
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
567 gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap); 567 gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap);
568 gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros(); 568 gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros();
569 gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors); 569 gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors);
570 gfx::Vector3dF principal = eigenvectors.get_column(0); 570 gfx::Vector3dF principal = eigenvectors.get_column(0);
571 if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF()) 571 if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF())
572 return false; // This may happen for some edge cases. 572 return false; // This may happen for some edge cases.
573 return ApplyColorReduction(source_bitmap, principal, true, target_bitmap); 573 return ApplyColorReduction(source_bitmap, principal, true, target_bitmap);
574 } 574 }
575 575
576 } // color_utils 576 } // color_utils
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698