// 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 #include #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; // 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) // 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; private: using ColorSets = std::forward_list; // 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 recipes_; }; } // namespace ui #endif // UI_COLOR_COLOR_MIXER_H_