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