blob: 5571b4e1b49302fcdadb227b28e4752c08a0717a [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_COLOR_COLOR_MIXER_H_
#define UI_COLOR_COLOR_MIXER_H_
#include <forward_list>
#include <map>
#include <set>
#include "base/callback_forward.h"
#include "base/callback_helpers.h"
#include "base/component_export.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/color/color_id.h"
#include "ui/color/color_set.h"
namespace ui {
class ColorRecipe;
// ColorMixer represents a single conceptual mapping of a set of inputs, via a
// collection of transforms, to a set of outputs. Examples of plausible
// ColorMixers are "the UI element colors, as constructed from core color
// primitive values", "the way a set of high contrast colors overwrites default
// values", "the final output colors for all parts of a single UI area", or
// "a layer that enforces contrast minima on a variety of inputs". ColorMixers
// are chained together into a pipeline by a ColorProvider, and thus may rely
// completely, partly, or not at all on the inputs and outputs of previous
// mixers in the pipeline.
class COMPONENT_EXPORT(COLOR) ColorMixer {
public:
using MixerGetter = base::RepeatingCallback<const ColorMixer*(void)>;
// Having each ColorMixer know about the |previous mixer| in the pipeline
// allows mixers to implement the pipeline directly and simplifies the API,
// compared to having each mixer report results (via e.g. Optional<SkColor>)
// to the ColorProvider, which would need to query different mixers in order.
// |input_mixer_getter| can be .Run() to obtain the appropriate mixer to
// query for transform inputs.
explicit ColorMixer(const ColorMixer* previous_mixer = nullptr,
MixerGetter input_mixer_getter = base::NullCallback());
// ColorMixer is movable since it holds both sets and recipes, each of which
// might be expensive to copy.
ColorMixer(ColorMixer&&) noexcept;
ColorMixer& operator=(ColorMixer&&) noexcept;
~ColorMixer();
// Adds a recipe for |id| if it does not exist.
ColorRecipe& operator[](ColorId id);
// Adds |set| to |sets_|. |set| must not have the same ID as any previously
// added sets, though it may contain colors with the same IDs as colors in
// those sets; in such cases, the last-added set takes priority.
void AddSet(ColorSet&& set);
// Returns the input color for |id|. First searches all |sets_| in reverse
// order; if not found, asks the previous mixer for the result color. If
// there is no previous mixer, returns gfx::kPlaceholderColor.
SkColor GetInputColor(ColorId id) const;
// Returns the color for |id| from |set_id|. If this mixer does not have that
// set, the request will be forwarded to the previous mixer. If there is no
// previous mixer, returns gfx::kPlaceholderColor.
SkColor GetOriginalColorFromSet(ColorId id, ColorSetId set_id) const;
// Returns the result color for |id|, that is, the result of applying any
// applicable recipe from |recipes_| to the relevant input color.
SkColor GetResultColor(ColorId id) const;
// Returns the ColorIds defined for this mixer.
std::set<ColorId> GetDefinedColorIds() const;
private:
using ColorSets = std::forward_list<ColorSet>;
// Returns an iterator to the set in |sets_| with ID |id|, or sets_.cend().
ColorSets::const_iterator FindSetWithId(ColorSetId id) const;
const ColorMixer* previous_mixer_;
MixerGetter input_mixer_getter_;
ColorSets sets_;
// This uses std::map instead of base::flat_map since the recipes are inserted
// one at a time instead of all at once, and there may be a lot of them.
// TODO(pkasting): Consider unifying how sets and recipes are specified:
// either both at construction (at which point this can use a flat_map) or
// both built piecemeal (which would mean ColorSets should probably become a
// std::map as well).
std::map<ColorId, ColorRecipe> recipes_;
};
} // namespace ui
#endif // UI_COLOR_COLOR_MIXER_H_