1 // Copyright 2019 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_COLOR_COLOR_MIXER_H_
6 #define UI_COLOR_COLOR_MIXER_H_
7
8 #include <forward_list>
9 #include <map>
10
11 #include "base/component_export.h"
12 #include "third_party/skia/include/core/SkColor.h"
13 #include "ui/color/color_id.h"
14 #include "ui/color/color_set.h"
15
16 namespace ui {
17
18 class ColorRecipe;
19
20 // ColorMixer represents a single conceptual mapping of a set of inputs, via a
21 // collection of transforms, to a set of outputs. Examples of plausible
22 // ColorMixers are "the UI element colors, as constructed from core color
23 // primitive values", "the way a set of high contrast colors overwrites default
24 // values", "the final output colors for all parts of a single UI area", or
25 // "a layer that enforces contrast minima on a variety of inputs". ColorMixers
26 // are chained together into a pipeline by a ColorProvider, and thus may rely
27 // completely, partly, or not at all on the inputs and outputs of previous
28 // mixers in the pipeline.
COMPONENT_EXPORT(COLOR)29 class COMPONENT_EXPORT(COLOR) ColorMixer {
30 public:
31 // Having each ColorMixer know about the |previous_mixer| in the pipeline
32 // allows mixers to implement the pipeline directly and simplifies the API,
33 // compared to having each mixer report results (via e.g. Optional<SkColor>)
34 // to the ColorProvider, which would need to query different mixers in order.
35 explicit ColorMixer(const ColorMixer* previous_mixer = nullptr);
36 // ColorMixer is movable since it holds both sets and recipes, each of which
37 // might be expensive to copy.
38 ColorMixer(ColorMixer&&);
39 ColorMixer& operator=(ColorMixer&&);
40 ~ColorMixer();
41
42 // Adds a recipe for |id| if it does not exist.
43 ColorRecipe& operator[](ColorId id);
44
45 // Adds |set| to |sets_|. |set| must not have the same ID as any previously
46 // added sets, though it may contain colors with the same IDs as colors in
47 // those sets; in such cases, the last-added set takes priority.
48 void AddSet(ColorSet&& set);
49
50 // Returns the input color for |id|. First searches all |sets_| in reverse
51 // order; if not found, asks the previous mixer for the result color. If
52 // there is no previous mixer, returns gfx::kPlaceholderColor.
53 SkColor GetInputColor(ColorId id) const;
54
55 // Returns the color for |id| from |set_id|. If this mixer does not have that
56 // set, the request will be forwarded to the previous mixer. If there is no
57 // previous mixer, returns gfx::kPlaceholderColor.
58 SkColor GetOriginalColorFromSet(ColorId id, ColorSetId set_id) const;
59
60 // Returns the result color for |id|, that is, the result of applying any
61 // applicable recipe from |recipes_| to the relevant input color.
62 SkColor GetResultColor(ColorId id) const;
63
64 private:
65 using ColorSets = std::forward_list<ColorSet>;
66
67 // Returns an iterator to the set in |sets_| with ID |id|, or sets_.cend().
68 ColorSets::const_iterator FindSetWithId(ColorSetId id) const;
69
70 const ColorMixer* previous_mixer_;
71 ColorSets sets_;
72
73 // This uses std::map instead of base::flat_map since the recipes are inserted
74 // one at a time instead of all at once, and there may be a lot of them.
75 // TODO(pkasting): Consider unifying how sets and recipes are specified:
76 // either both at construction (at which point this can use a flat_map) or
77 // both built piecemeal (which would mean ColorSets should probably become a
78 // std::map as well).
79 std::map<ColorId, ColorRecipe> recipes_;
80 };
81
82 } // namespace ui
83
84 #endif // UI_COLOR_COLOR_MIXER_H_
85