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 #ifndef UI_GFX_COLOR_ANALYSIS_H_ 6 #define UI_GFX_COLOR_ANALYSIS_H_ 7 8 #include <stdint.h> 9 10 #include "base/callback_forward.h" 11 #include "base/compiler_specific.h" 12 #include "base/memory/ref_counted.h" 13 #include "base/memory/ref_counted_memory.h" 14 #include "third_party/skia/include/core/SkColor.h" 15 #include "ui/gfx/geometry/matrix3_f.h" 16 #include "ui/gfx/gfx_export.h" 17 18 class SkBitmap; 19 20 namespace gfx { 21 class Rect; 22 } // namespace gfx 23 24 namespace color_utils { 25 26 struct HSL; 27 28 // This class exposes the sampling method to the caller, which allows 29 // stubbing out for things like unit tests. Might be useful to pass more 30 // arguments into the GetSample method in the future (such as which 31 // cluster is being worked on, etc.). 32 // 33 // Note: Samplers should be deterministic, as the same image may be analyzed 34 // twice with two sampler instances and the results displayed side-by-side 35 // to the user. 36 class GFX_EXPORT KMeanImageSampler { 37 public: 38 virtual int GetSample(int width, int height) = 0; 39 40 protected: 41 KMeanImageSampler(); 42 virtual ~KMeanImageSampler(); 43 }; 44 45 // This sampler will pick pixels from an evenly spaced grid. 46 class GFX_EXPORT GridSampler : public KMeanImageSampler { 47 public: 48 GridSampler(); 49 ~GridSampler() override; 50 51 int GetSample(int width, int height) override; 52 53 private: 54 // The number of times GetSample has been called. 55 int calls_; 56 }; 57 58 // Returns the color in an ARGB |image| that is closest in RGB-space to the 59 // provided |color|. Exported for testing. 60 GFX_EXPORT SkColor FindClosestColor(const uint8_t* image, int width, int height, 61 SkColor color); 62 63 // Returns an SkColor that represents the calculated dominant color in the 64 // image. This uses a KMean clustering algorithm to find clusters of pixel 65 // colors in RGB space. 66 // |png|/|bitmap| represents the data of a png/bitmap encoded image. 67 // |lower_bound| represents the minimum bound of HSL values to allow. 68 // |upper_bound| represents the maximum bound of HSL values to allow. 69 // See color_utils::IsWithinHSLRange() for description of these bounds. 70 // 71 // RGB KMean Algorithm (N clusters, M iterations): 72 // 1.Pick N starting colors by randomly sampling the pixels. If you see a 73 // color you already saw keep sampling. After a certain number of tries 74 // just remove the cluster and continue with N = N-1 clusters (for an image 75 // with just one color this should devolve to N=1). These colors are the 76 // centers of your N clusters. 77 // 2.For each pixel in the image find the cluster that it is closest to in RGB 78 // space. Add that pixel's color to that cluster (we keep a sum and a count 79 // of all of the pixels added to the space, so just add it to the sum and 80 // increment count). 81 // 3.Calculate the new cluster centroids by getting the average color of all of 82 // the pixels in each cluster (dividing the sum by the count). 83 // 4.See if the new centroids are the same as the old centroids. 84 // a) If this is the case for all N clusters than we have converged and 85 // can move on. 86 // b) If any centroid moved, repeat step 2 with the new centroids for up 87 // to M iterations. 88 // 5.Once the clusters have converged or M iterations have been tried, sort 89 // the clusters by weight (where weight is the number of pixels that make up 90 // this cluster). 91 // 6.Going through the sorted list of clusters, pick the first cluster with the 92 // largest weight that's centroid falls between |lower_bound| and 93 // |upper_bound|. Return that color. 94 // If no color fulfills that requirement return the color with the largest 95 // weight regardless of whether or not it fulfills the equation above. 96 GFX_EXPORT SkColor 97 CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png, 98 const HSL& lower_bound, 99 const HSL& upper_bound, 100 KMeanImageSampler* sampler); 101 // Computes a dominant color using the above algorithm and reasonable defaults 102 // for |lower_bound|, |upper_bound| and |sampler|. 103 GFX_EXPORT SkColor CalculateKMeanColorOfPNG( 104 scoped_refptr<base::RefCountedMemory> png); 105 106 // Computes a dominant color for the first |height| rows of |bitmap| using the 107 // above algorithm and a reasonable default sampler. If |find_closest| is true, 108 // the returned color will be the closest color to the true K-mean color that 109 // actually appears in the image; if false, the true color is returned 110 // regardless of whether it actually appears. 111 GFX_EXPORT SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap, 112 int height, 113 const HSL& lower_bound, 114 const HSL& upper_bound, 115 bool find_closest); 116 117 // Computes a dominant color using the above algorithm and reasonable defaults 118 // for |lower_bound|, |upper_bound| and |sampler|. 119 GFX_EXPORT SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap); 120 121 // These enums specify general values to look for when calculating prominent 122 // colors from an image. For example, a "light vibrant" prominent color would 123 // tend to be brighter and more saturated. The best combination of color 124 // attributes depends on how you plan to apply the color. 125 enum class LumaRange { 126 ANY, 127 LIGHT, 128 NORMAL, 129 DARK, 130 }; 131 132 enum class SaturationRange { 133 ANY, 134 VIBRANT, 135 MUTED, 136 }; 137 138 struct ColorProfile { 139 ColorProfile() = default; ColorProfileColorProfile140 ColorProfile(LumaRange l, SaturationRange s) : luma(l), saturation(s) {} 141 142 LumaRange luma = LumaRange::DARK; 143 SaturationRange saturation = SaturationRange::MUTED; 144 }; 145 146 // A color value with an associated weight. 147 struct Swatch { SwatchSwatch148 Swatch() : Swatch(SK_ColorTRANSPARENT, 0) {} 149 SwatchSwatch150 Swatch(SkColor color, size_t population) 151 : color(color), population(population) {} 152 153 SkColor color; 154 155 // The population correlates to a count, so it should be 1 or greater. 156 size_t population; 157 158 bool operator==(const Swatch& other) const { 159 return color == other.color && population == other.population; 160 } 161 }; 162 163 // Used to filter colors from swatches. Called with the candidate color and will 164 // return true if the color should be allowed. 165 using ColorSwatchFilter = base::RepeatingCallback<bool(const SkColor&)>; 166 167 // The maximum number of pixels to consider when generating swatches. 168 GFX_EXPORT extern const int kMaxConsideredPixelsForSwatches; 169 170 // Returns a vector of |Swatch| that represent the prominent colors of the 171 // bitmap within |region|. The |max_swatches| is the maximum number of swatches. 172 // For landscapes, good values are in the range 12-16. For images which are 173 // largely made up of people's faces then this value should be increased to 174 // 24-32. |filter| is an optional filter that can filter out unwanted colors. 175 // This is an implementation of the Android Palette API: 176 // https://developer.android.com/reference/android/support/v7/graphics/Palette 177 GFX_EXPORT std::vector<Swatch> CalculateColorSwatches( 178 const SkBitmap& bitmap, 179 size_t max_swatches, 180 const gfx::Rect& region, 181 base::Optional<ColorSwatchFilter> filter); 182 183 // Returns a vector of RGB colors that represents the bitmap based on the 184 // |color_profiles| provided. For each value, if a value is succesfully 185 // calculated, the calculated value is fully opaque. For failure, the calculated 186 // value is transparent. |region| can be provided to select a specific area of 187 // the bitmap. |filter| is an optional filter that can filter out unwanted 188 // colors. If |filter| is not provided then we will filter out uninteresting 189 // colors. 190 GFX_EXPORT std::vector<Swatch> CalculateProminentColorsOfBitmap( 191 const SkBitmap& bitmap, 192 const std::vector<ColorProfile>& color_profiles, 193 gfx::Rect* region, 194 ColorSwatchFilter filter); 195 196 // Compute color covariance matrix for the input bitmap. 197 GFX_EXPORT gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap); 198 199 // Apply a color reduction transform defined by |color_transform| vector to 200 // |source_bitmap|. The result is put into |target_bitmap|, which is expected 201 // to be initialized to the required size and type (SkBitmap::kA8_Config). 202 // If |fit_to_range|, result is transfored linearly to fit 0-0xFF range. 203 // Otherwise, data is clipped. 204 // Returns true if the target has been computed. 205 GFX_EXPORT bool ApplyColorReduction(const SkBitmap& source_bitmap, 206 const gfx::Vector3dF& color_transform, 207 bool fit_to_range, 208 SkBitmap* target_bitmap); 209 210 } // namespace color_utils 211 212 #endif // UI_GFX_COLOR_ANALYSIS_H_ 213