Commit 971df2a0 authored by Tom Anderson's avatar Tom Anderson Committed by Commit Bot

[GTK] Enforce a minimum tab stroke contrast ratio

BUG=853841
R=pkasting

Change-Id: Ifb680152768ea9214861b63b9f55181c2ad1ed6b
Reviewed-on: https://chromium-review.googlesource.com/c/1278144Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600180}
parent 7d1b8312
...@@ -663,56 +663,37 @@ bool ThemeService::HasCustomImage(int id) const { ...@@ -663,56 +663,37 @@ bool ThemeService::HasCustomImage(int id) const {
// static // static
SkColor ThemeService::GetSeparatorColor(SkColor tab_color, SkColor ThemeService::GetSeparatorColor(SkColor tab_color,
SkColor frame_color) { SkColor frame_color) {
// We use this alpha value for the separator if possible. const float kContrastRatio = 2.f;
const SkAlpha kAlpha = 0x40;
// In most cases, if the tab is lighter than the frame, we darken the // In most cases, if the tab is lighter than the frame, we darken the
// frame; if the tab is darker than the frame, we lighten the frame. // frame; if the tab is darker than the frame, we lighten the frame.
// However, if the frame is already very dark or very light, respectively, // However, if the frame is already very dark or very light, respectively,
// this won't contrast sufficiently with the frame color, so we'll need to // this won't contrast sufficiently with the frame color, so we'll need to
// reverse when we're lightening and darkening. // reverse when we're lightening and darkening.
const float tab_luminance = color_utils::GetRelativeLuminance(tab_color); const bool lighten = color_utils::GetRelativeLuminance(tab_color) <
const float frame_luminance = color_utils::GetRelativeLuminance(frame_color); color_utils::GetRelativeLuminance(frame_color);
const bool lighten = tab_luminance < frame_luminance; SkColor separator_color =
SkColor separator_color = lighten ? SK_ColorWHITE : SK_ColorBLACK; lighten ? SK_ColorWHITE : color_utils::GetDarkestColor();
float separator_luminance = color_utils::GetRelativeLuminance(
color_utils::AlphaBlend(separator_color, frame_color, kAlpha)); SkAlpha alpha = color_utils::FindBlendValueForContrastRatio(
// The minimum contrast ratio here is just under the ~1.1469 in the default MD frame_color, separator_color, frame_color, kContrastRatio, 0);
// incognito theme. We want the separator to still darken the frame in that if (color_utils::GetContrastRatio(
// theme, but that's about as low of contrast as we're willing to accept. color_utils::AlphaBlend(separator_color, frame_color, alpha),
const float kMinContrastRatio = 1.1465f; frame_color) >= kContrastRatio) {
if (color_utils::GetContrastRatio(separator_luminance, frame_luminance) >= return SkColorSetA(separator_color, alpha);
kMinContrastRatio)
return SkColorSetA(separator_color, kAlpha);
// We need to reverse whether we're darkening or lightening. We know the new
// separator color will contrast with the frame; check whether it also
// contrasts at least as well with the tab.
separator_color = color_utils::InvertColor(separator_color);
separator_luminance = color_utils::GetRelativeLuminance(
color_utils::AlphaBlend(separator_color, frame_color, kAlpha));
if (color_utils::GetContrastRatio(separator_luminance, tab_luminance) >=
color_utils::GetContrastRatio(separator_luminance, frame_luminance))
return SkColorSetA(separator_color, kAlpha);
// The reversed separator doesn't contrast enough with the tab. Compute the
// resulting luminance from adjusting the tab color, instead of the frame
// color, by the separator color.
const float target_luminance = color_utils::GetRelativeLuminance(
color_utils::AlphaBlend(separator_color, tab_color, kAlpha));
// Now try to compute an alpha for the separator such that, when blended with
// the frame, it results in the above luminance. Because the luminance
// computation is not easily invertible, we use a binary search over the
// possible range of alpha values.
SkAlpha alpha = 128;
for (int delta = lighten ? 64 : -64; delta != 0; delta /= 2) {
const float luminance = color_utils::GetRelativeLuminance(
color_utils::AlphaBlend(separator_color, frame_color, alpha));
if (luminance == target_luminance)
break;
alpha += (luminance < target_luminance) ? -delta : delta;
} }
separator_color =
color_utils::BlendTowardOppositeLuma(separator_color, SK_AlphaOPAQUE);
// If the above call failed to create sufficient contrast, the frame color is
// already very dark or very light. Since separators are only used when the
// tab has low contrast against the frame, the tab color is similarly very
// dark or very light, just not quite as much so as the frame color. Blend
// towards the opposite separator color, and compute the contrast against the
// tab instead of the frame to ensure both contrasts hit the desired minimum.
alpha = color_utils::FindBlendValueForContrastRatio(
frame_color, separator_color, tab_color, kContrastRatio, 0);
return SkColorSetA(separator_color, alpha); return SkColorSetA(separator_color, alpha);
} }
......
...@@ -327,57 +327,6 @@ views::LinuxUI::NonClientWindowFrameAction GetDefaultMiddleClickAction() { ...@@ -327,57 +327,6 @@ views::LinuxUI::NonClientWindowFrameAction GetDefaultMiddleClickAction() {
} }
} }
// COLOR_TOOLBAR_TOP_SEPARATOR represents the border between tabs and the
// frame, as well as the border between tabs and the toolbar. For this
// reason, it is difficult to calculate the One True Color that works well on
// all themes and is opaque. However, we can cheat to get a good color that
// works well for both borders. The idea is we have two variables: alpha and
// lightness. And we have two constraints (on lightness):
// 1. the border color, when painted on |header_bg|, should give |header_fg|
// 2. the border color, when painted on |tab_bg|, should give |tab_fg|
// This gives the equations:
// alpha*lightness + (1 - alpha)*header_bg = header_fg
// alpha*lightness + (1 - alpha)*tab_bg = tab_fg
// The algorithm below is just a result of solving those equations for alpha
// and lightness. If a problem is encountered, like division by zero, or
// |a| or |l| not in [0, 1], then fallback on |header_fg| or |tab_fg|.
SkColor GetToolbarTopSeparatorColor(SkColor header_fg,
SkColor header_bg,
SkColor tab_fg,
SkColor tab_bg) {
using namespace color_utils;
SkColor default_color = SkColorGetA(header_fg) ? header_fg : tab_fg;
if (!SkColorGetA(default_color))
return SK_ColorTRANSPARENT;
auto get_lightness = [](SkColor color) {
HSL hsl;
SkColorToHSL(color, &hsl);
return hsl.l;
};
double f1 = get_lightness(GetResultingPaintColor(header_fg, header_bg));
double b1 = get_lightness(header_bg);
double f2 = get_lightness(GetResultingPaintColor(tab_fg, tab_bg));
double b2 = get_lightness(tab_bg);
if (b1 == b2)
return default_color;
double a = (f1 - f2 - b1 + b2) / (b2 - b1);
if (a == 0)
return default_color;
double l = (f1 - (1 - a) * b1) / a;
if (a < 0 || a > 1 || l < 0 || l > 1)
return default_color;
// Take the hue and saturation from |default_color|, but use the
// calculated lightness.
HSL border;
SkColorToHSL(default_color, &border);
border.l = l;
return HSLToSkColor(border, a * 0xff);
}
} // namespace } // namespace
GtkUi::GtkUi() { GtkUi::GtkUi() {
...@@ -1010,22 +959,36 @@ void GtkUi::UpdateColors() { ...@@ -1010,22 +959,36 @@ void GtkUi::UpdateColors() {
// These colors represent the border drawn around tabs and between // These colors represent the border drawn around tabs and between
// the tabstrip and toolbar. // the tabstrip and toolbar.
SkColor toolbar_top_separator = SkColor toolbar_top_separator = GetBorderColor(
GetBorderColor(header_selector + " GtkButton#button"); header_selector + " GtkSeparator#separator.vertical.titlebutton");
SkColor toolbar_top_separator_inactive = SkColor toolbar_top_separator_inactive =
GetBorderColor(header_selector +
":backdrop GtkSeparator#separator.vertical.titlebutton");
auto toolbar_top_separator_has_good_contrast = [&]() {
// This constant is copied from chrome/browser/themes/theme_service.cc.
const float kMinContrastRatio = 2.f;
SkColor active = color_utils::GetResultingPaintColor(
toolbar_top_separator, frame_color);
SkColor inactive = color_utils::GetResultingPaintColor(
toolbar_top_separator_inactive, frame_color_inactive);
return color_utils::GetContrastRatio(frame_color, active) >=
kMinContrastRatio &&
color_utils::GetContrastRatio(frame_color_inactive, inactive) >=
kMinContrastRatio;
};
if (!toolbar_top_separator_has_good_contrast()) {
toolbar_top_separator =
GetBorderColor(header_selector + " GtkButton#button");
toolbar_top_separator_inactive =
GetBorderColor(header_selector + ":backdrop GtkButton#button"); GetBorderColor(header_selector + ":backdrop GtkButton#button");
if (!ui::MaterialDesignController::IsRefreshUi()) {
toolbar_top_separator = GetToolbarTopSeparatorColor(
toolbar_top_separator, frame_color, tab_border, tab_color);
toolbar_top_separator_inactive = GetToolbarTopSeparatorColor(
toolbar_top_separator_inactive, frame_color_inactive, tab_border,
tab_color);
} }
// Unlike with toolbars, we always want a border around tabs, so let // If we can't get a contrasting stroke from the theme, have ThemeService
// ThemeService choose the border color if the theme doesn't provide one. // provide a stroke color for us.
if (SkColorGetA(toolbar_top_separator) && if (toolbar_top_separator_has_good_contrast()) {
SkColorGetA(toolbar_top_separator_inactive)) {
color_map[ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR] = color_map[ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR] =
toolbar_top_separator; toolbar_top_separator;
color_map[ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR_INACTIVE] = color_map[ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR_INACTIVE] =
...@@ -1097,6 +1060,9 @@ void GtkUi::UpdateDefaultFont() { ...@@ -1097,6 +1060,9 @@ void GtkUi::UpdateDefaultFont() {
} }
void GtkUi::ResetStyle() { void GtkUi::ResetStyle() {
colors_.clear();
custom_frame_colors_.clear();
native_frame_colors_.clear();
LoadGtkValues(); LoadGtkValues();
native_theme_->NotifyObservers(); native_theme_->NotifyObservers();
} }
......
...@@ -443,7 +443,7 @@ void SetDarkestColor(SkColor color) { ...@@ -443,7 +443,7 @@ void SetDarkestColor(SkColor color) {
g_color_utils_luma_midpoint = (GetLuma(color) + 255) / 2; g_color_utils_luma_midpoint = (GetLuma(color) + 255) / 2;
} }
SkColor GetDarkestColorForTesting() { SkColor GetDarkestColor() {
return g_color_utils_darkest; return g_color_utils_darkest;
} }
......
...@@ -189,7 +189,7 @@ GFX_EXPORT std::string SkColorToRgbString(SkColor color); ...@@ -189,7 +189,7 @@ GFX_EXPORT std::string SkColorToRgbString(SkColor color);
GFX_EXPORT void SetDarkestColor(SkColor color); GFX_EXPORT void SetDarkestColor(SkColor color);
// Returns the current color_utils darkest color so tests can clean up. // Returns the current color_utils darkest color so tests can clean up.
GFX_EXPORT SkColor GetDarkestColorForTesting(); GFX_EXPORT SkColor GetDarkestColor();
} // namespace color_utils } // namespace color_utils
......
...@@ -205,7 +205,7 @@ TEST(ColorUtils, SkColorToRgbString) { ...@@ -205,7 +205,7 @@ TEST(ColorUtils, SkColorToRgbString) {
} }
TEST(ColorUtils, IsDarkDarkestColorChange) { TEST(ColorUtils, IsDarkDarkestColorChange) {
SkColor old_black_color = GetDarkestColorForTesting(); SkColor old_black_color = GetDarkestColor();
ASSERT_FALSE(IsDark(SkColorSetARGB(255, 200, 200, 200))); ASSERT_FALSE(IsDark(SkColorSetARGB(255, 200, 200, 200)));
SetDarkestColor(SkColorSetARGB(255, 200, 200, 200)); SetDarkestColor(SkColorSetARGB(255, 200, 200, 200));
...@@ -236,7 +236,7 @@ TEST(ColorUtils, GetColorWithMinimumContrast_BlendLighter) { ...@@ -236,7 +236,7 @@ TEST(ColorUtils, GetColorWithMinimumContrast_BlendLighter) {
} }
TEST(ColorUtils, GetColorWithMinimumContrast_StopsAtDarkestColor) { TEST(ColorUtils, GetColorWithMinimumContrast_StopsAtDarkestColor) {
SkColor old_black_color = GetDarkestColorForTesting(); SkColor old_black_color = GetDarkestColor();
const SkColor darkest_color = SkColorSetRGB(0x44, 0x44, 0x44); const SkColor darkest_color = SkColorSetRGB(0x44, 0x44, 0x44);
SetDarkestColor(darkest_color); SetDarkestColor(darkest_color);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment