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