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_classifier.h"
6 
7 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
8 
9 namespace blink {
10 namespace {
11 
12 class SimpleColorClassifier : public DarkModeColorClassifier {
13  public:
NeverInvert()14   static std::unique_ptr<SimpleColorClassifier> NeverInvert() {
15     return std::unique_ptr<SimpleColorClassifier>(
16         new SimpleColorClassifier(DarkModeClassification::kDoNotApplyFilter));
17   }
18 
AlwaysInvert()19   static std::unique_ptr<SimpleColorClassifier> AlwaysInvert() {
20     return std::unique_ptr<SimpleColorClassifier>(
21         new SimpleColorClassifier(DarkModeClassification::kApplyFilter));
22   }
23 
ShouldInvertColor(const Color & color)24   DarkModeClassification ShouldInvertColor(const Color& color) override {
25     return value_;
26   }
27 
28  private:
SimpleColorClassifier(DarkModeClassification value)29   SimpleColorClassifier(DarkModeClassification value) : value_(value) {}
30 
31   DarkModeClassification value_;
32 };
33 
34 class InvertLowBrightnessColorsClassifier : public DarkModeColorClassifier {
35  public:
InvertLowBrightnessColorsClassifier(int brightness_threshold)36   InvertLowBrightnessColorsClassifier(int brightness_threshold)
37       : brightness_threshold_(brightness_threshold) {
38     DCHECK_GT(brightness_threshold_, 0);
39     DCHECK_LT(brightness_threshold_, 256);
40   }
41 
ShouldInvertColor(const Color & color)42   DarkModeClassification ShouldInvertColor(const Color& color) override {
43     if (CalculateColorBrightness(color) < brightness_threshold_)
44       return DarkModeClassification::kApplyFilter;
45     return DarkModeClassification::kDoNotApplyFilter;
46   }
47 
48  private:
49   int brightness_threshold_;
50 };
51 
52 class InvertHighBrightnessColorsClassifier : public DarkModeColorClassifier {
53  public:
InvertHighBrightnessColorsClassifier(int brightness_threshold)54   InvertHighBrightnessColorsClassifier(int brightness_threshold)
55       : brightness_threshold_(brightness_threshold) {
56     DCHECK_GT(brightness_threshold_, 0);
57     DCHECK_LT(brightness_threshold_, 256);
58   }
59 
ShouldInvertColor(const Color & color)60   DarkModeClassification ShouldInvertColor(const Color& color) override {
61     if (CalculateColorBrightness(color) > brightness_threshold_)
62       return DarkModeClassification::kApplyFilter;
63     return DarkModeClassification::kDoNotApplyFilter;
64   }
65 
66  private:
67   int brightness_threshold_;
68 };
69 
70 }  // namespace
71 
72 // Based on this algorithm suggested by the W3:
73 // https://www.w3.org/TR/AERT/#color-contrast
74 //
75 // We don't use HSL or HSV here because perceived brightness is a function of
76 // hue as well as lightness/value.
CalculateColorBrightness(const Color & color)77 int DarkModeColorClassifier::CalculateColorBrightness(const Color& color) {
78   int weighted_red = color.Red() * 299;
79   int weighted_green = color.Green() * 587;
80   int weighted_blue = color.Blue() * 114;
81   return (weighted_red + weighted_green + weighted_blue) / 1000;
82 }
83 
84 std::unique_ptr<DarkModeColorClassifier>
MakeTextColorClassifier(const DarkModeSettings & settings)85 DarkModeColorClassifier::MakeTextColorClassifier(
86     const DarkModeSettings& settings) {
87   DCHECK_LE(settings.text_brightness_threshold, 256);
88   DCHECK_GE(settings.text_brightness_threshold, 0);
89 
90   // The value should be between 0 and 256, but check for values outside that
91   // range here to preserve correct behavior in non-debug builds.
92   if (settings.text_brightness_threshold >= 256)
93     return SimpleColorClassifier::AlwaysInvert();
94   if (settings.text_brightness_threshold <= 0)
95     return SimpleColorClassifier::NeverInvert();
96 
97   return std::make_unique<InvertLowBrightnessColorsClassifier>(
98       settings.text_brightness_threshold);
99 }
100 
101 std::unique_ptr<DarkModeColorClassifier>
MakeBackgroundColorClassifier(const DarkModeSettings & settings)102 DarkModeColorClassifier::MakeBackgroundColorClassifier(
103     const DarkModeSettings& settings) {
104   DCHECK_LE(settings.background_brightness_threshold, 256);
105   DCHECK_GE(settings.background_brightness_threshold, 0);
106 
107   // The value should be between 0 and 256, but check for values outside that
108   // range here to preserve correct behavior in non-debug builds.
109   if (settings.background_brightness_threshold >= 256)
110     return SimpleColorClassifier::NeverInvert();
111   if (settings.background_brightness_threshold <= 0)
112     return SimpleColorClassifier::AlwaysInvert();
113 
114   return std::make_unique<InvertHighBrightnessColorsClassifier>(
115       settings.background_brightness_threshold);
116 }
117 
~DarkModeColorClassifier()118 DarkModeColorClassifier::~DarkModeColorClassifier() {}
119 
120 }  // namespace blink
121