Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ui / gfx / color_analysis.cc
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
3 // found in the LICENSE file.
4
5 #include "ui/gfx/color_analysis.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <vector>
10
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "third_party/skia/include/core/SkBitmap.h"
14 #include "third_party/skia/include/core/SkColorPriv.h"
15 #include "third_party/skia/include/core/SkUnPreMultiply.h"
16 #include "ui/gfx/codec/png_codec.h"
17 #include "ui/gfx/color_utils.h"
18
19 namespace color_utils {
20 namespace {
21
22 // RGBA KMean Constants
23 const uint32_t kNumberOfClusters = 4;
24 const int kNumberOfIterations = 50;
25
26 const HSL kDefaultLowerHSLBound = {-1, -1, 0.15};
27 const HSL kDefaultUpperHSLBound = {-1, -1, 0.85};
28
29 // Background Color Modification Constants
30 const SkColor kDefaultBgColor = SK_ColorWHITE;
31
32 // Support class to hold information about each cluster of pixel data in
33 // the KMean algorithm. While this class does not contain all of the points
34 // that exist in the cluster, it keeps track of the aggregate sum so it can
35 // compute the new center appropriately.
36 class KMeanCluster {
37  public:
38   KMeanCluster() {
39     Reset();
40   }
41
42   void Reset() {
43     centroid[0] = centroid[1] = centroid[2] = 0;
44     aggregate[0] = aggregate[1] = aggregate[2] = 0;
45     counter = 0;
46     weight = 0;
47   }
48
49   inline void SetCentroid(uint8_t r, uint8_t g, uint8_t b) {
50     centroid[0] = r;
51     centroid[1] = g;
52     centroid[2] = b;
53   }
54
55   inline void GetCentroid(uint8_t* r, uint8_t* g, uint8_t* b) {
56     *r = centroid[0];
57     *g = centroid[1];
58     *b = centroid[2];
59   }
60
61   inline bool IsAtCentroid(uint8_t r, uint8_t g, uint8_t b) {
62     return r == centroid[0] && g == centroid[1] && b == centroid[2];
63   }
64
65   // Recomputes the centroid of the cluster based on the aggregate data. The
66   // number of points used to calculate this center is stored for weighting
67   // purposes. The aggregate and counter are then cleared to be ready for the
68   // next iteration.
69   inline void RecomputeCentroid() {
70     if (counter > 0) {
71       centroid[0] = aggregate[0] / counter;
72       centroid[1] = aggregate[1] / counter;
73       centroid[2] = aggregate[2] / counter;
74
75       aggregate[0] = aggregate[1] = aggregate[2] = 0;
76       weight = counter;
77       counter = 0;
78     }
79   }
80
81   inline void AddPoint(uint8_t r, uint8_t g, uint8_t b) {
82     aggregate[0] += r;
83     aggregate[1] += g;
84     aggregate[2] += b;
85     ++counter;
86   }
87
88   // Just returns the distance^2. Since we are comparing relative distances
89   // there is no need to perform the expensive sqrt() operation.
90   inline uint32_t GetDistanceSqr(uint8_t r, uint8_t g, uint8_t b) {
91     return (r - centroid[0]) * (r - centroid[0]) +
92            (g - centroid[1]) * (g - centroid[1]) +
93            (b - centroid[2]) * (b - centroid[2]);
94   }
95
96   // In order to determine if we have hit convergence or not we need to see
97   // if the centroid of the cluster has moved. This determines whether or
98   // not the centroid is the same as the aggregate sum of points that will be
99   // used to generate the next centroid.
100   inline bool CompareCentroidWithAggregate() {
101     if (counter == 0)
102       return false;
103
104     return aggregate[0] / counter == centroid[0] &&
105            aggregate[1] / counter == centroid[1] &&
106            aggregate[2] / counter == centroid[2];
107   }
108
109   // Returns the previous counter, which is used to determine the weight
110   // of the cluster for sorting.
111   inline uint32_t GetWeight() const {
112     return weight;
113   }
114
115   static bool SortKMeanClusterByWeight(const KMeanCluster& a,
116                                        const KMeanCluster& b) {
117     return a.GetWeight() > b.GetWeight();
118   }
119
120  private:
121   uint8_t centroid[3];
122
123   // Holds the sum of all the points that make up this cluster. Used to
124   // generate the next centroid as well as to check for convergence.
125   uint32_t aggregate[3];
126   uint32_t counter;
127
128   // The weight of the cluster, determined by how many points were used
129   // to generate the previous centroid.
130   uint32_t weight;
131 };
132
133 // Un-premultiplies each pixel in |bitmap| into an output |buffer|.
134 void UnPreMultiply(const SkBitmap& bitmap, uint32_t* buffer, int buffer_size) {
135   SkAutoLockPixels auto_lock(bitmap);
136   uint32_t* in = static_cast<uint32_t*>(bitmap.getPixels());
137   uint32_t* out = buffer;
138   int pixel_count = std::min(bitmap.width() * bitmap.height(), buffer_size);
139   for (int i = 0; i < pixel_count; ++i) {
140     int alpha = SkGetPackedA32(*in);
141     if (alpha != 0 && alpha != 255)
142       *out++ = SkUnPreMultiply::PMColorToColor(*in++);
143     else
144       *out++ = *in++;
145   }
146 }
147
148 } // namespace
149
150 KMeanImageSampler::KMeanImageSampler() {
151 }
152
153 KMeanImageSampler::~KMeanImageSampler() {
154 }
155
156 GridSampler::GridSampler() : calls_(0) {
157 }
158
159 GridSampler::~GridSampler() {
160 }
161
162 int GridSampler::GetSample(int width, int height) {
163   // Hand-drawn bitmaps often have special outlines or feathering at the edges.
164   // Start our sampling inset from the top and left edges. For example, a 10x10
165   // image with 4 clusters would be sampled like this:
166   // ..........
167   // .0.4.8....
168   // ..........
169   // .1.5.9....
170   // ..........
171   // .2.6......
172   // ..........
173   // .3.7......
174   // ..........
175   const int kPadX = 1;
176   const int kPadY = 1;
177   int x = kPadX +
178       (calls_ / kNumberOfClusters) * ((width - 2 * kPadX) / kNumberOfClusters);
179   int y = kPadY +
180       (calls_ % kNumberOfClusters) * ((height - 2 * kPadY) / kNumberOfClusters);
181   int index = x + (y * width);
182   ++calls_;
183   return index % (width * height);
184 }
185
186 SkColor FindClosestColor(const uint8_t* image,
187                          int width,
188                          int height,
189                          SkColor color) {
190   uint8_t in_r = SkColorGetR(color);
191   uint8_t in_g = SkColorGetG(color);
192   uint8_t in_b = SkColorGetB(color);
193   // Search using distance-squared to avoid expensive sqrt() operations.
194   int best_distance_squared = kint32max;
195   SkColor best_color = color;
196   const uint8_t* byte = image;
197   for (int i = 0; i < width * height; ++i) {
198     uint8_t b = *(byte++);
199     uint8_t g = *(byte++);
200     uint8_t r = *(byte++);
201     uint8_t a = *(byte++);
202     // Ignore fully transparent pixels.
203     if (a == 0)
204       continue;
205     int distance_squared =
206         (in_b - b) * (in_b - b) +
207         (in_g - g) * (in_g - g) +
208         (in_r - r) * (in_r - r);
209     if (distance_squared < best_distance_squared) {
210       best_distance_squared = distance_squared;
211       best_color = SkColorSetRGB(r, g, b);
212     }
213   }
214   return best_color;
215 }
216
217 // For a 16x16 icon on an Intel Core i5 this function takes approximately
218 // 0.5 ms to run.
219 // TODO(port): This code assumes the CPU architecture is little-endian.
220 SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data,
221                                     int img_width,
222                                     int img_height,
223                                     const HSL& lower_bound,
224                                     const HSL& upper_bound,
225                                     KMeanImageSampler* sampler) {
226   SkColor color = kDefaultBgColor;
227   if (img_width > 0 && img_height > 0) {
228     std::vector<KMeanCluster> clusters;
229     clusters.resize(kNumberOfClusters, KMeanCluster());
230
231     // Pick a starting point for each cluster
232     std::vector<KMeanCluster>::iterator cluster = clusters.begin();
233     while (cluster != clusters.end()) {
234       // Try up to 10 times to find a unique color. If no unique color can be
235       // found, destroy this cluster.
236       bool color_unique = false;
237       for (int i = 0; i < 10; ++i) {
238         int pixel_pos = sampler->GetSample(img_width, img_height) %
239             (img_width * img_height);
240
241         uint8_t b = decoded_data[pixel_pos * 4];
242         uint8_t g = decoded_data[pixel_pos * 4 + 1];
243         uint8_t r = decoded_data[pixel_pos * 4 + 2];
244         uint8_t a = decoded_data[pixel_pos * 4 + 3];
245         // Skip fully transparent pixels as they usually contain black in their
246         // RGB channels but do not contribute to the visual image.
247         if (a == 0)
248           continue;
249
250         // Loop through the previous clusters and check to see if we have seen
251         // this color before.
252         color_unique = true;
253         for (std::vector<KMeanCluster>::iterator
254             cluster_check = clusters.begin();
255             cluster_check != cluster; ++cluster_check) {
256           if (cluster_check->IsAtCentroid(r, g, b)) {
257             color_unique = false;
258             break;
259           }
260         }
261
262         // If we have a unique color set the center of the cluster to
263         // that color.
264         if (color_unique) {
265           cluster->SetCentroid(r, g, b);
266           break;
267         }
268       }
269
270       // If we don't have a unique color erase this cluster.
271       if (!color_unique) {
272         cluster = clusters.erase(cluster);
273       } else {
274         // Have to increment the iterator here, otherwise the increment in the
275         // for loop will skip a cluster due to the erase if the color wasn't
276         // unique.
277         ++cluster;
278       }
279     }
280
281     // If all pixels in the image are transparent we will have no clusters.
282     if (clusters.empty())
283       return color;
284
285     bool convergence = false;
286     for (int iteration = 0;
287         iteration < kNumberOfIterations && !convergence;
288         ++iteration) {
289
290       // Loop through each pixel so we can place it in the appropriate cluster.
291       uint8_t* pixel = decoded_data;
292       uint8_t* decoded_data_end = decoded_data + (img_width * img_height * 4);
293       while (pixel < decoded_data_end) {
294         uint8_t b = *(pixel++);
295         uint8_t g = *(pixel++);
296         uint8_t r = *(pixel++);
297         uint8_t a = *(pixel++);
298         // Skip transparent pixels, see above.
299         if (a == 0)
300           continue;
301
302         uint32_t distance_sqr_to_closest_cluster = UINT_MAX;
303         std::vector<KMeanCluster>::iterator closest_cluster = clusters.begin();
304
305         // Figure out which cluster this color is closest to in RGB space.
306         for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
307             cluster != clusters.end(); ++cluster) {
308           uint32_t distance_sqr = cluster->GetDistanceSqr(r, g, b);
309
310           if (distance_sqr < distance_sqr_to_closest_cluster) {
311             distance_sqr_to_closest_cluster = distance_sqr;
312             closest_cluster = cluster;
313           }
314         }
315
316         closest_cluster->AddPoint(r, g, b);
317       }
318
319       // Calculate the new cluster centers and see if we've converged or not.
320       convergence = true;
321       for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
322           cluster != clusters.end(); ++cluster) {
323         convergence &= cluster->CompareCentroidWithAggregate();
324
325         cluster->RecomputeCentroid();
326       }
327     }
328
329     // Sort the clusters by population so we can tell what the most popular
330     // color is.
331     std::sort(clusters.begin(), clusters.end(),
332               KMeanCluster::SortKMeanClusterByWeight);
333
334     // Loop through the clusters to figure out which cluster has an appropriate
335     // color. Skip any that are too bright/dark and go in order of weight.
336     for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
337         cluster != clusters.end(); ++cluster) {
338       uint8_t r, g, b;
339       cluster->GetCentroid(&r, &g, &b);
340
341       SkColor current_color = SkColorSetARGB(SK_AlphaOPAQUE, r, g, b);
342       HSL hsl;
343       SkColorToHSL(current_color, &hsl);
344       if (IsWithinHSLRange(hsl, lower_bound, upper_bound)) {
345         // If we found a valid color just set it and break. We don't want to
346         // check the other ones.
347         color = current_color;
348         break;
349       } else if (cluster == clusters.begin()) {
350         // We haven't found a valid color, but we are at the first color so
351         // set the color anyway to make sure we at least have a value here.
352         color = current_color;
353       }
354     }
355   }
356
357   // Find a color that actually appears in the image (the K-mean cluster center
358   // will not usually be a color that appears in the image).
359   return FindClosestColor(decoded_data, img_width, img_height, color);
360 }
361
362 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png,
363                                  const HSL& lower_bound,
364                                  const HSL& upper_bound,
365                                  KMeanImageSampler* sampler) {
366   int img_width = 0;
367   int img_height = 0;
368   std::vector<uint8_t> decoded_data;
369   SkColor color = kDefaultBgColor;
370
371   if (png.get() && png->size() &&
372       gfx::PNGCodec::Decode(png->front(),
373                             png->size(),
374                             gfx::PNGCodec::FORMAT_BGRA,
375                             &decoded_data,
376                             &img_width,
377                             &img_height)) {
378     return CalculateKMeanColorOfBuffer(&decoded_data[0],
379                                        img_width,
380                                        img_height,
381                                        lower_bound,
382                                        upper_bound,
383                                        sampler);
384   }
385   return color;
386 }
387
388 SkColor CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png) {
389   GridSampler sampler;
390   return CalculateKMeanColorOfPNG(
391       png, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler);
392 }
393
394 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap,
395                                     const HSL& lower_bound,
396                                     const HSL& upper_bound,
397                                     KMeanImageSampler* sampler) {
398   // SkBitmap uses pre-multiplied alpha but the KMean clustering function
399   // above uses non-pre-multiplied alpha. Transform the bitmap before we
400   // analyze it because the function reads each pixel multiple times.
401   int pixel_count = bitmap.width() * bitmap.height();
402   scoped_ptr<uint32_t[]> image(new uint32_t[pixel_count]);
403   UnPreMultiply(bitmap, image.get(), pixel_count);
404
405   return CalculateKMeanColorOfBuffer(reinterpret_cast<uint8_t*>(image.get()),
406                                      bitmap.width(),
407                                      bitmap.height(),
408                                      lower_bound,
409                                      upper_bound,
410                                      sampler);
411 }
412
413 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) {
414   GridSampler sampler;
415   return CalculateKMeanColorOfBitmap(
416       bitmap, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler);
417 }
418
419 gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) {
420   // First need basic stats to normalize each channel separately.
421   SkAutoLockPixels bitmap_lock(bitmap);
422   gfx::Matrix3F covariance = gfx::Matrix3F::Zeros();
423   if (!bitmap.getPixels())
424     return covariance;
425
426   // Assume ARGB_8888 format.
427   DCHECK(bitmap.colorType() == kN32_SkColorType);
428
429   int64_t r_sum = 0;
430   int64_t g_sum = 0;
431   int64_t b_sum = 0;
432   int64_t rr_sum = 0;
433   int64_t gg_sum = 0;
434   int64_t bb_sum = 0;
435   int64_t rg_sum = 0;
436   int64_t rb_sum = 0;
437   int64_t gb_sum = 0;
438
439   for (int y = 0; y < bitmap.height(); ++y) {
440     SkPMColor* current_color = static_cast<uint32_t*>(bitmap.getAddr32(0, y));
441     for (int x = 0; x < bitmap.width(); ++x, ++current_color) {
442       SkColor c;
443       int alpha = SkGetPackedA32(*current_color);
444       if (alpha != 0 && alpha != 255)
445         c = SkUnPreMultiply::PMColorToColor(*current_color);
446       else
447         c = *current_color;
448
449       SkColor r = SkColorGetR(c);
450       SkColor g = SkColorGetG(c);
451       SkColor b = SkColorGetB(c);
452
453       r_sum += r;
454       g_sum += g;
455       b_sum += b;
456       rr_sum += r * r;
457       gg_sum += g * g;
458       bb_sum += b * b;
459       rg_sum += r * g;
460       rb_sum += r * b;
461       gb_sum += g * b;
462     }
463   }
464
465   // Covariance (not normalized) is E(X*X.t) - m * m.t and this is how it
466   // is calculated below.
467   // Each row below represents a row of the matrix describing (co)variances
468   // of R, G and B channels with (R, G, B)
469   int pixel_n = bitmap.width() * bitmap.height();
470   covariance.set(
471       (static_cast<double>(rr_sum) / pixel_n -
472        static_cast<double>(r_sum * r_sum) / pixel_n / pixel_n),
473       (static_cast<double>(rg_sum) / pixel_n -
474        static_cast<double>(r_sum * g_sum) / pixel_n / pixel_n),
475       (static_cast<double>(rb_sum) / pixel_n -
476        static_cast<double>(r_sum * b_sum) / pixel_n / pixel_n),
477       (static_cast<double>(rg_sum) / pixel_n -
478        static_cast<double>(r_sum * g_sum) / pixel_n / pixel_n),
479       (static_cast<double>(gg_sum) / pixel_n -
480        static_cast<double>(g_sum * g_sum) / pixel_n / pixel_n),
481       (static_cast<double>(gb_sum) / pixel_n -
482        static_cast<double>(g_sum * b_sum) / pixel_n / pixel_n),
483       (static_cast<double>(rb_sum) / pixel_n -
484        static_cast<double>(r_sum * b_sum) / pixel_n / pixel_n),
485       (static_cast<double>(gb_sum) / pixel_n -
486        static_cast<double>(g_sum * b_sum) / pixel_n / pixel_n),
487       (static_cast<double>(bb_sum) / pixel_n -
488        static_cast<double>(b_sum * b_sum) / pixel_n / pixel_n));
489   return covariance;
490 }
491
492 bool ApplyColorReduction(const SkBitmap& source_bitmap,
493                          const gfx::Vector3dF& color_transform,
494                          bool fit_to_range,
495                          SkBitmap* target_bitmap) {
496   DCHECK(target_bitmap);
497   SkAutoLockPixels source_lock(source_bitmap);
498   SkAutoLockPixels target_lock(*target_bitmap);
499
500   DCHECK(source_bitmap.getPixels());
501   DCHECK(target_bitmap->getPixels());
502   DCHECK_EQ(kN32_SkColorType, source_bitmap.colorType());
503   DCHECK_EQ(kAlpha_8_SkColorType, target_bitmap->colorType());
504   DCHECK_EQ(source_bitmap.height(), target_bitmap->height());
505   DCHECK_EQ(source_bitmap.width(), target_bitmap->width());
506   DCHECK(!source_bitmap.empty());
507
508   // Elements of color_transform are explicitly off-loaded to local values for
509   // efficiency reasons. Note that in practice images may correspond to entire
510   // tab captures.
511   float t0 = 0.0;
512   float tr = color_transform.x();
513   float tg = color_transform.y();
514   float tb = color_transform.z();
515
516   if (fit_to_range) {
517     // We will figure out min/max in a preprocessing step and adjust
518     // actual_transform as required.
519     float max_val = std::numeric_limits<float>::min();
520     float min_val = std::numeric_limits<float>::max();
521     for (int y = 0; y < source_bitmap.height(); ++y) {
522       const SkPMColor* source_color_row = static_cast<SkPMColor*>(
523           source_bitmap.getAddr32(0, y));
524       for (int x = 0; x < source_bitmap.width(); ++x) {
525         SkColor c;
526         int alpha = SkGetPackedA32(source_color_row[x]);
527         if (alpha != 0 && alpha != 255)
528           c = SkUnPreMultiply::PMColorToColor(source_color_row[x]);
529         else
530           c = source_color_row[x];
531
532         float r = SkColorGetR(c);
533         float g = SkColorGetG(c);
534         float b = SkColorGetB(c);
535         float gray_level = tr * r + tg * g + tb * b;
536         max_val = std::max(max_val, gray_level);
537         min_val = std::min(min_val, gray_level);
538       }
539     }
540
541     // Adjust the transform so that the result is scaling.
542     float scale = 0.0;
543     t0 = -min_val;
544     if (max_val > min_val)
545       scale = 255.0 / (max_val - min_val);
546     t0 *= scale;
547     tr *= scale;
548     tg *= scale;
549     tb *= scale;
550   }
551
552   for (int y = 0; y < source_bitmap.height(); ++y) {
553     const SkPMColor* source_color_row = static_cast<SkPMColor*>(
554         source_bitmap.getAddr32(0, y));
555     uint8_t* target_color_row = target_bitmap->getAddr8(0, y);
556     for (int x = 0; x < source_bitmap.width(); ++x) {
557       SkColor c;
558       int alpha = SkGetPackedA32(source_color_row[x]);
559       if (alpha != 0 && alpha != 255)
560         c = SkUnPreMultiply::PMColorToColor(source_color_row[x]);
561       else
562         c = source_color_row[x];
563
564       float r = SkColorGetR(c);
565       float g = SkColorGetG(c);
566       float b = SkColorGetB(c);
567
568       float gl = t0 + tr * r + tg * g + tb * b;
569       if (gl < 0)
570         gl = 0;
571       if (gl > 0xFF)
572         gl = 0xFF;
573       target_color_row[x] = static_cast<uint8_t>(gl);
574     }
575   }
576
577   return true;
578 }
579
580 bool ComputePrincipalComponentImage(const SkBitmap& source_bitmap,
581                                     SkBitmap* target_bitmap) {
582   if (!target_bitmap) {
583     NOTREACHED();
584     return false;
585   }
586
587   gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap);
588   gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros();
589   gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors);
590   gfx::Vector3dF principal = eigenvectors.get_column(0);
591   if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF())
592     return false;  // This may happen for some edge cases.
593   return ApplyColorReduction(source_bitmap, principal, true, target_bitmap);
594 }
595
596 }  // color_utils