| // 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. |
| |
| #include "chrome/common/themes/autogenerated_theme_util.h" |
| |
| #include "ui/gfx/color_utils.h" |
| |
| // Decreases the lightness of the given color. |
| SkColor DarkenColor(SkColor color, float change) { |
| color_utils::HSL hsl; |
| SkColorToHSL(color, &hsl); |
| hsl.l -= change; |
| if (hsl.l >= 0.0f) |
| return HSLToSkColor(hsl, 255); |
| return color; |
| } |
| |
| // Increases the lightness of |source| until it reaches |contrast_ratio| with |
| // |base| or reaches |white_contrast| with white. This avoids decreasing |
| // saturation, as the alternative contrast-guaranteeing functions in color_utils |
| // would do. |
| SkColor LightenUntilContrast(SkColor source, |
| SkColor base, |
| float contrast_ratio, |
| float white_contrast) { |
| const float kBaseLuminance = color_utils::GetRelativeLuminance(base); |
| constexpr float kWhiteLuminance = 1.0f; |
| |
| color_utils::HSL hsl; |
| SkColorToHSL(source, &hsl); |
| float min_l = hsl.l; |
| float max_l = 1.0f; |
| |
| // Need only precision of 2 digits. |
| while (max_l - min_l > 0.01) { |
| hsl.l = min_l + (max_l - min_l) / 2; |
| float luminance = color_utils::GetRelativeLuminance(HSLToSkColor(hsl, 255)); |
| if (color_utils::GetContrastRatio(kBaseLuminance, luminance) >= |
| contrast_ratio || |
| (color_utils::GetContrastRatio(kWhiteLuminance, luminance) < |
| white_contrast)) { |
| max_l = hsl.l; |
| } else { |
| min_l = hsl.l; |
| } |
| } |
| |
| hsl.l = max_l; |
| return HSLToSkColor(hsl, 255); |
| } |
| |
| AutogeneratedThemeColors GetAutogeneratedThemeColors(SkColor color) { |
| SkColor frame_color = color; |
| SkColor frame_text_color; |
| SkColor active_tab_color = color; |
| SkColor active_tab_text_color; |
| |
| constexpr float kDarkenStep = 0.03f; |
| constexpr float kMinWhiteContrast = 1.3f; |
| constexpr float kNoWhiteContrast = 0.0f; |
| |
| // Used to determine what we consider a very dark color. |
| constexpr float kMaxLuminosityForDark = 0.05f; |
| |
| // Increasingly darken frame color and calculate the rest until colors with |
| // sufficient contrast are found. |
| while (true) { |
| // Calculate frame color to have sufficient contrast with white or dark grey |
| // text. |
| frame_text_color = color_utils::GetColorWithMaxContrast(frame_color); |
| SkColor blend_target = |
| color_utils::GetColorWithMaxContrast(frame_text_color); |
| frame_color = color_utils::BlendForMinContrast( |
| frame_color, frame_text_color, blend_target, |
| kAutogeneratedThemeTextPreferredContrast) |
| .color; |
| |
| // Generate active tab color so that it has enough contrast with the |
| // |frame_color| to avoid the isolation line in the tab strip. |
| active_tab_color = LightenUntilContrast( |
| frame_color, frame_color, kAutogeneratedThemeActiveTabMinContrast, |
| kNoWhiteContrast); |
| |
| // We want more contrast between frame and active tab for dark colors. |
| color_utils::HSL hsl; |
| SkColorToHSL(frame_color, &hsl); |
| float preferred_contrast = |
| hsl.l <= kMaxLuminosityForDark |
| ? kAutogeneratedThemeActiveTabPreferredContrastForDark |
| : kAutogeneratedThemeActiveTabPreferredContrast; |
| |
| // Try lightening the color to get more contrast with frame without getting |
| // too close to white. |
| active_tab_color = LightenUntilContrast( |
| active_tab_color, frame_color, preferred_contrast, kMinWhiteContrast); |
| |
| // If we didn't succeed in generating active tab color with minimum |
| // contrast with frame, then darken the frame color and try again. |
| if (color_utils::GetContrastRatio(frame_color, active_tab_color) < |
| kAutogeneratedThemeActiveTabMinContrast) { |
| frame_color = DarkenColor(frame_color, kDarkenStep); |
| continue; |
| } |
| |
| // Select active tab text color, if possible. |
| active_tab_text_color = |
| color_utils::GetColorWithMaxContrast(active_tab_color); |
| |
| if (!color_utils::IsDark(active_tab_color)) { |
| // If active tab is light color then continue lightening it until enough |
| // contrast with dark text is reached. |
| active_tab_text_color = |
| color_utils::GetColorWithMaxContrast(active_tab_color); |
| active_tab_color = LightenUntilContrast( |
| active_tab_color, active_tab_text_color, |
| kAutogeneratedThemeTextPreferredContrast, kNoWhiteContrast); |
| break; |
| } |
| |
| // If the active tab color is dark and has enough contrast with white text. |
| // Then we are all set. |
| if (color_utils::GetContrastRatio(active_tab_color, SK_ColorWHITE) >= |
| kAutogeneratedThemeTextPreferredContrast) |
| break; |
| |
| // If the active tab color is a dark color but the contrast with white is |
| // not enough then we should darken the active tab color to reach the |
| // contrast with white. But to keep the contrast with the frame we should |
| // also darken the frame color. Therefore, just darken the frame color and |
| // try again. |
| frame_color = DarkenColor(frame_color, kDarkenStep); |
| } |
| return {frame_color, frame_text_color, active_tab_color, |
| active_tab_text_color}; |
| } |