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 "chrome/common/themes/autogenerated_theme_util.h"
6
7 #include "ui/gfx/color_utils.h"
8
9 // Decreases the lightness of the given color.
DarkenColor(SkColor color,float change)10 SkColor DarkenColor(SkColor color, float change) {
11 color_utils::HSL hsl;
12 SkColorToHSL(color, &hsl);
13 hsl.l -= change;
14 if (hsl.l >= 0.0f)
15 return HSLToSkColor(hsl, 255);
16 return color;
17 }
18
19 // Increases the lightness of |source| until it reaches |contrast_ratio| with
20 // |base| or reaches |white_contrast| with white. This avoids decreasing
21 // saturation, as the alternative contrast-guaranteeing functions in color_utils
22 // would do.
LightenUntilContrast(SkColor source,SkColor base,float contrast_ratio,float white_contrast)23 SkColor LightenUntilContrast(SkColor source,
24 SkColor base,
25 float contrast_ratio,
26 float white_contrast) {
27 const float kBaseLuminance = color_utils::GetRelativeLuminance(base);
28 constexpr float kWhiteLuminance = 1.0f;
29
30 color_utils::HSL hsl;
31 SkColorToHSL(source, &hsl);
32 float min_l = hsl.l;
33 float max_l = 1.0f;
34
35 // Need only precision of 2 digits.
36 while (max_l - min_l > 0.01) {
37 hsl.l = min_l + (max_l - min_l) / 2;
38 float luminance = color_utils::GetRelativeLuminance(HSLToSkColor(hsl, 255));
39 if (color_utils::GetContrastRatio(kBaseLuminance, luminance) >=
40 contrast_ratio ||
41 (color_utils::GetContrastRatio(kWhiteLuminance, luminance) <
42 white_contrast)) {
43 max_l = hsl.l;
44 } else {
45 min_l = hsl.l;
46 }
47 }
48
49 hsl.l = max_l;
50 return HSLToSkColor(hsl, 255);
51 }
52
GetAutogeneratedThemeColors(SkColor color)53 AutogeneratedThemeColors GetAutogeneratedThemeColors(SkColor color) {
54 SkColor frame_color = color;
55 SkColor frame_text_color;
56 SkColor active_tab_color = color;
57 SkColor active_tab_text_color;
58
59 constexpr float kDarkenStep = 0.03f;
60 constexpr float kMinWhiteContrast = 1.3f;
61 constexpr float kNoWhiteContrast = 0.0f;
62
63 // Used to determine what we consider a very dark color.
64 constexpr float kMaxLuminosityForDark = 0.05f;
65
66 // Increasingly darken frame color and calculate the rest until colors with
67 // sufficient contrast are found.
68 while (true) {
69 // Calculate frame color to have sufficient contrast with white or dark grey
70 // text.
71 frame_text_color = color_utils::GetColorWithMaxContrast(frame_color);
72 SkColor blend_target =
73 color_utils::GetColorWithMaxContrast(frame_text_color);
74 frame_color = color_utils::BlendForMinContrast(
75 frame_color, frame_text_color, blend_target,
76 kAutogeneratedThemeTextPreferredContrast)
77 .color;
78
79 // Generate active tab color so that it has enough contrast with the
80 // |frame_color| to avoid the isolation line in the tab strip.
81 active_tab_color = LightenUntilContrast(
82 frame_color, frame_color, kAutogeneratedThemeActiveTabMinContrast,
83 kNoWhiteContrast);
84
85 // We want more contrast between frame and active tab for dark colors.
86 color_utils::HSL hsl;
87 SkColorToHSL(frame_color, &hsl);
88 float preferred_contrast =
89 hsl.l <= kMaxLuminosityForDark
90 ? kAutogeneratedThemeActiveTabPreferredContrastForDark
91 : kAutogeneratedThemeActiveTabPreferredContrast;
92
93 // Try lightening the color to get more contrast with frame without getting
94 // too close to white.
95 active_tab_color = LightenUntilContrast(
96 active_tab_color, frame_color, preferred_contrast, kMinWhiteContrast);
97
98 // If we didn't succeed in generating active tab color with minimum
99 // contrast with frame, then darken the frame color and try again.
100 if (color_utils::GetContrastRatio(frame_color, active_tab_color) <
101 kAutogeneratedThemeActiveTabMinContrast) {
102 frame_color = DarkenColor(frame_color, kDarkenStep);
103 continue;
104 }
105
106 // Select active tab text color, if possible.
107 active_tab_text_color =
108 color_utils::GetColorWithMaxContrast(active_tab_color);
109
110 if (!color_utils::IsDark(active_tab_color)) {
111 // If active tab is light color then continue lightening it until enough
112 // contrast with dark text is reached.
113 active_tab_text_color =
114 color_utils::GetColorWithMaxContrast(active_tab_color);
115 active_tab_color = LightenUntilContrast(
116 active_tab_color, active_tab_text_color,
117 kAutogeneratedThemeTextPreferredContrast, kNoWhiteContrast);
118 break;
119 }
120
121 // If the active tab color is dark and has enough contrast with white text.
122 // Then we are all set.
123 if (color_utils::GetContrastRatio(active_tab_color, SK_ColorWHITE) >=
124 kAutogeneratedThemeTextPreferredContrast)
125 break;
126
127 // If the active tab color is a dark color but the contrast with white is
128 // not enough then we should darken the active tab color to reach the
129 // contrast with white. But to keep the contrast with the frame we should
130 // also darken the frame color. Therefore, just darken the frame color and
131 // try again.
132 frame_color = DarkenColor(frame_color, kDarkenStep);
133 }
134 return {frame_color, frame_text_color, active_tab_color,
135 active_tab_text_color};
136 }
137