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 #include "third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h"
6
7 #include "base/logging.h"
8 #include "third_party/blink/renderer/platform/graphics/lab_color_space.h"
9 #include "third_party/skia/include/core/SkColorFilter.h"
10 #include "third_party/skia/include/effects/SkHighContrastFilter.h"
11 #include "third_party/skia/include/effects/SkTableColorFilter.h"
12
13 namespace blink {
14 namespace {
15
SkColorFilterFromSettings(SkHighContrastConfig::InvertStyle invert_style,const DarkModeSettings & settings)16 sk_sp<SkColorFilter> SkColorFilterFromSettings(
17 SkHighContrastConfig::InvertStyle invert_style,
18 const DarkModeSettings& settings) {
19 SkHighContrastConfig config;
20 config.fInvertStyle = invert_style;
21 config.fGrayscale = settings.grayscale;
22 config.fContrast = settings.contrast;
23 return SkHighContrastFilter::Make(config);
24 }
25
26 // Further darken dark grays to match the primary surface color recommended by
27 // the material design guidelines:
28 // https://material.io/design/color/dark-theme.html#properties
29 //
30 // TODO(gilmanmh): Consider adding a more general way to adjust colors after
31 // applying the main filter.
AdjustGray(Color * color)32 void AdjustGray(Color* color) {
33 DCHECK(color);
34 static const int kBrightnessThreshold = 32;
35 static const int kAdjustedBrightness = 18;
36
37 if (color->Red() == color->Blue() && color->Red() == color->Green() &&
38 color->Red() < kBrightnessThreshold &&
39 color->Red() > kAdjustedBrightness) {
40 color->SetRGB(kAdjustedBrightness, kAdjustedBrightness,
41 kAdjustedBrightness);
42 }
43 }
44
45 class SkColorFilterWrapper : public DarkModeColorFilter {
46 public:
SkColorFilterWrapper(sk_sp<SkColorFilter> filter)47 SkColorFilterWrapper(sk_sp<SkColorFilter> filter) : filter_(filter) {}
48
InvertColor(const Color & color) const49 Color InvertColor(const Color& color) const override {
50 return Color(filter_->filterColor(color.Rgb()));
51 }
52
ToSkColorFilter() const53 sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
54
55 private:
56 sk_sp<SkColorFilter> filter_;
57 };
58
59 class LabColorFilter : public DarkModeColorFilter {
60 public:
LabColorFilter()61 LabColorFilter() : transformer_(LabColorSpace::RGBLABTransformer()) {
62 SkHighContrastConfig config;
63 config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
64 config.fGrayscale = false;
65 config.fContrast = 0.0;
66 filter_ = SkHighContrastFilter::Make(config);
67 }
68
InvertColor(const Color & color) const69 Color InvertColor(const Color& color) const override {
70 blink::FloatPoint3D rgb = {color.Red() / 255.0f, color.Green() / 255.0f,
71 color.Blue() / 255.0f};
72 blink::FloatPoint3D lab = transformer_.sRGBToLab(rgb);
73 float invertedL = std::min(110.0f - lab.X(), 100.0f);
74 lab.SetX(invertedL);
75 rgb = transformer_.LabToSRGB(lab);
76
77 Color inverted_color(Color(static_cast<unsigned int>(rgb.X() * 255 + 0.5),
78 static_cast<unsigned int>(rgb.Y() * 255 + 0.5),
79 static_cast<unsigned int>(rgb.Z() * 255 + 0.5),
80 color.Alpha()));
81 AdjustGray(&inverted_color);
82 return inverted_color;
83 }
84
ToSkColorFilter() const85 sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
86
87 private:
88 const LabColorSpace::RGBLABTransformer transformer_;
89 sk_sp<SkColorFilter> filter_;
90 };
91
92 } // namespace
93
FromSettings(const DarkModeSettings & settings)94 std::unique_ptr<DarkModeColorFilter> DarkModeColorFilter::FromSettings(
95 const DarkModeSettings& settings) {
96 switch (settings.mode) {
97 case DarkModeInversionAlgorithm::kOff:
98 return nullptr;
99
100 case DarkModeInversionAlgorithm::kSimpleInvertForTesting:
101 uint8_t identity[256], invert[256];
102 for (int i = 0; i < 256; ++i) {
103 identity[i] = i;
104 invert[i] = 255 - i;
105 }
106 return std::make_unique<SkColorFilterWrapper>(
107 SkTableColorFilter::MakeARGB(identity, invert, invert, invert));
108
109 case DarkModeInversionAlgorithm::kInvertBrightness:
110 return std::make_unique<SkColorFilterWrapper>(SkColorFilterFromSettings(
111 SkHighContrastConfig::InvertStyle::kInvertBrightness, settings));
112
113 case DarkModeInversionAlgorithm::kInvertLightness:
114 return std::make_unique<SkColorFilterWrapper>(SkColorFilterFromSettings(
115 SkHighContrastConfig::InvertStyle::kInvertLightness, settings));
116
117 case DarkModeInversionAlgorithm::kInvertLightnessLAB:
118 return std::make_unique<LabColorFilter>();
119 }
120 NOTREACHED();
121 }
122
~DarkModeColorFilter()123 DarkModeColorFilter::~DarkModeColorFilter() {}
124
125 } // namespace blink
126