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