blob: d587948791e54cd6e2f650234ada4ad26dec8872 [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 "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:
// 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.
explicit ColorMixer(const ColorMixer* previous_mixer = nullptr);
// 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 |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);
// Adds a recipe for |id|, which must not already have an existing recipe.
// Returns a non-const ref to allow chaining calls to
// ColorRecipe::AddTransform().
ColorRecipe& AddRecipe(ColorId id);
// 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<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_;
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_