| // 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 "ui/native_theme/caption_style.h" |
| |
| #include <Windows.Media.ClosedCaptioning.h> |
| #include <wrl/client.h> |
| |
| #include <string> |
| |
| #include "base/check_op.h" |
| #include "base/notreached.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/win/core_winrt_util.h" |
| #include "base/win/windows_version.h" |
| #include "skia/ext/skia_utils_win.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/gfx/color_utils.h" |
| |
| namespace CC = ABI::Windows::Media::ClosedCaptioning; |
| |
| namespace ui { |
| |
| namespace { |
| |
| // Adds !important to all captions styles. They should always override any |
| // styles added by the video author or by a user stylesheet. This is because on |
| // Windows, there is an option to turn off captions styles, so any time the |
| // captions are on, the styles should take priority. |
| std::string AddCSSImportant(std::string css_string) { |
| return css_string + " !important"; |
| } |
| |
| // Translates a Windows::Media::ClosedCaptioning::ClosedCaptionStyle to a |
| // CSS FontFamily value. |
| // These fonts were chosen to satisfy the characteristics represented by values |
| // of ClosedCaptionStyle Enum in Windows Settings. |
| void GetFontFamilyString(CC::ClosedCaptionStyle closed_caption_style, |
| std::string* css_font_family, |
| std::string* css_font_variant) { |
| *css_font_variant = "normal"; |
| switch (closed_caption_style) { |
| case CC::ClosedCaptionStyle_MonospacedWithSerifs: |
| *css_font_family = "Courier New"; |
| break; |
| case CC::ClosedCaptionStyle_ProportionalWithSerifs: |
| *css_font_family = "Times New Roman"; |
| break; |
| case CC::ClosedCaptionStyle_MonospacedWithoutSerifs: |
| *css_font_family = "Consolas"; |
| break; |
| case CC::ClosedCaptionStyle_ProportionalWithoutSerifs: |
| *css_font_family = "Tahoma"; |
| break; |
| case CC::ClosedCaptionStyle_Casual: |
| *css_font_family = "Segoe Print"; |
| break; |
| case CC::ClosedCaptionStyle_Cursive: |
| *css_font_family = "Segoe Script"; |
| break; |
| case CC::ClosedCaptionStyle_SmallCapitals: |
| *css_font_family = "Tahoma"; |
| *css_font_variant = "small-caps"; |
| break; |
| case CC::ClosedCaptionStyle_Default: |
| // We shouldn't override with OS Styling for Default case. |
| NOTREACHED(); |
| *css_font_family = std::string(); |
| break; |
| } |
| } |
| |
| // Translates a Windows::Media::ClosedCaptioning::ClosedCaptionEdgeEffect to a |
| // CSS style value. |
| std::string GetEdgeEffectString(CC::ClosedCaptionEdgeEffect edge_effect) { |
| switch (edge_effect) { |
| case CC::ClosedCaptionEdgeEffect_None: |
| return "none"; |
| case CC::ClosedCaptionEdgeEffect_Raised: |
| return "-1px 0px 0px silver, 0px -1px 0px silver, 1px 1px 0px black, 2px " |
| "2px 0px black, 3px 3px 0px black"; |
| case CC::ClosedCaptionEdgeEffect_Depressed: |
| return "1px 1px 0px silver, 0px 1px 0px silver, -1px -1px 0px black, " |
| "-1px " |
| "0px 0px black"; |
| case CC::ClosedCaptionEdgeEffect_Uniform: |
| return "0px 0px 4px black, 0px 0px 4px black, 0px 0px 4px black, 0px 0px " |
| "4px black"; |
| case CC::ClosedCaptionEdgeEffect_DropShadow: |
| return "0px 0px 2px rgba(0, 0, 0, 0.5), 2px 2px 2px black"; |
| case CC::ClosedCaptionEdgeEffect_Default: |
| // We shouldn't override with OS Styling for Default case. |
| NOTREACHED(); |
| return std::string(); |
| } |
| } |
| |
| // Translates a Windows::Media::ClosedCaptioning::ClosedCaptionSize to a CSS |
| // style value. |
| std::string GetCaptionSizeString(CC::ClosedCaptionSize caption_size) { |
| switch (caption_size) { |
| case CC::ClosedCaptionSize_FiftyPercent: |
| return "50%"; |
| case CC::ClosedCaptionSize_OneHundredPercent: |
| return "100%"; |
| case CC::ClosedCaptionSize_OneHundredFiftyPercent: |
| return "150%"; |
| case CC::ClosedCaptionSize_TwoHundredPercent: |
| return "200%"; |
| case CC::ClosedCaptionSize_Default: |
| // We shouldn't override with OS Styling for Default case. |
| NOTREACHED(); |
| return std::string(); |
| } |
| } |
| |
| // Translates a Windows::Media::ClosedCaptioning::ClosedCaptionOpacity to an |
| // SkAlpha value. |
| SkAlpha GetCaptionOpacity(CC::ClosedCaptionOpacity caption_opacity) { |
| switch (caption_opacity) { |
| case CC::ClosedCaptionOpacity_ZeroPercent: |
| return SK_AlphaTRANSPARENT; |
| case CC::ClosedCaptionOpacity_TwentyFivePercent: |
| return base::ClampRound<SkAlpha>(SK_AlphaOPAQUE * 0.25); |
| case CC::ClosedCaptionOpacity_SeventyFivePercent: |
| return base::ClampRound<SkAlpha>(SK_AlphaOPAQUE * 0.75); |
| case CC::ClosedCaptionOpacity_OneHundredPercent: |
| case CC::ClosedCaptionOpacity_Default: |
| default: |
| return SK_AlphaOPAQUE; |
| } |
| } |
| |
| // Translates a Windows::Media::ClosedCaptioning::ClosedCaptionColor to an |
| // SkColor value. |
| SkColor GetCaptionColor(CC::ClosedCaptionColor caption_color) { |
| switch (caption_color) { |
| case CC::ClosedCaptionColor_Black: |
| return SK_ColorBLACK; |
| case CC::ClosedCaptionColor_Red: |
| return SK_ColorRED; |
| case CC::ClosedCaptionColor_Green: |
| return SK_ColorGREEN; |
| case CC::ClosedCaptionColor_Blue: |
| return SK_ColorBLUE; |
| case CC::ClosedCaptionColor_Yellow: |
| return SK_ColorYELLOW; |
| case CC::ClosedCaptionColor_Magenta: |
| return SK_ColorMAGENTA; |
| case CC::ClosedCaptionColor_Cyan: |
| return SK_ColorCYAN; |
| case CC::ClosedCaptionColor_White: |
| return SK_ColorWHITE; |
| case CC::ClosedCaptionColor_Default: |
| default: |
| // We shouldn't override with OS Styling for Default case. |
| NOTREACHED(); |
| return SK_ColorWHITE; |
| } |
| } |
| |
| // Translates a Windows::Media::ClosedCaptioning::ClosedCaptionColor and a |
| // Windows::Media::ClosedCaptioning::ClosedCaptionOpacity to an RGBA CSS color |
| // string. |
| std::string GetCssColorWithAlpha(CC::ClosedCaptionColor caption_color, |
| CC::ClosedCaptionOpacity caption_opacity) { |
| const SkAlpha opacity = GetCaptionOpacity(caption_opacity); |
| const SkColor color = GetCaptionColor(caption_color); |
| return color_utils::SkColorToRgbaString(SkColorSetA(color, opacity)); |
| } |
| |
| absl::optional<CaptionStyle> InitializeFromSystemSettings() { |
| TRACE_EVENT0("ui", "InitializeFromSystemSettings"); |
| DCHECK_GE(base::win::GetVersion(), base::win::Version::WIN10); |
| DCHECK(base::FeatureList::IsEnabled(features::kSystemCaptionStyle)); |
| |
| // Need to do this check before using ScopedHString. |
| bool can_use_scoped_hstring = |
| base::win::ResolveCoreWinRTDelayload() && |
| base::win::ScopedHString::ResolveCoreWinRTStringDelayload(); |
| |
| if (!can_use_scoped_hstring) |
| return absl::nullopt; |
| |
| base::win::ScopedHString closed_caption_properties_string = |
| base::win::ScopedHString::Create( |
| RuntimeClass_Windows_Media_ClosedCaptioning_ClosedCaptionProperties); |
| Microsoft::WRL::ComPtr<CC::IClosedCaptionPropertiesStatics> |
| closed_caption_properties_statics; |
| HRESULT hr = base::win::RoGetActivationFactory( |
| closed_caption_properties_string.get(), |
| IID_PPV_ARGS(&closed_caption_properties_statics)); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionSize font_size = CC::ClosedCaptionSize_Default; |
| hr = closed_caption_properties_statics->get_FontSize(&font_size); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionEdgeEffect edge_effect = CC::ClosedCaptionEdgeEffect_Default; |
| hr = closed_caption_properties_statics->get_FontEffect(&edge_effect); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionStyle font_family = CC::ClosedCaptionStyle_Default; |
| hr = closed_caption_properties_statics->get_FontStyle(&font_family); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionColor font_color = CC::ClosedCaptionColor_Default; |
| hr = closed_caption_properties_statics->get_FontColor(&font_color); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionOpacity font_opacity = CC::ClosedCaptionOpacity_Default; |
| hr = closed_caption_properties_statics->get_FontOpacity(&font_opacity); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionColor background_color = CC::ClosedCaptionColor_Default; |
| hr = |
| closed_caption_properties_statics->get_BackgroundColor(&background_color); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionOpacity background_opacity = |
| CC::ClosedCaptionOpacity_Default; |
| hr = closed_caption_properties_statics->get_BackgroundOpacity( |
| &background_opacity); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionColor region_color = CC::ClosedCaptionColor_Default; |
| hr = closed_caption_properties_statics->get_RegionColor(®ion_color); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CC::ClosedCaptionOpacity region_opacity = CC::ClosedCaptionOpacity_Default; |
| hr = closed_caption_properties_statics->get_RegionOpacity(®ion_opacity); |
| if (FAILED(hr)) |
| return absl::nullopt; |
| |
| CaptionStyle caption_style; |
| if (font_family != CC::ClosedCaptionStyle_Default) { |
| GetFontFamilyString(font_family, &(caption_style.font_family), |
| &(caption_style.font_variant)); |
| caption_style.font_family = AddCSSImportant(caption_style.font_family); |
| caption_style.font_variant = AddCSSImportant(caption_style.font_variant); |
| } |
| |
| if (font_size != CC::ClosedCaptionSize_Default) |
| caption_style.text_size = AddCSSImportant(GetCaptionSizeString(font_size)); |
| |
| if (edge_effect != CC::ClosedCaptionEdgeEffect_Default) { |
| caption_style.text_shadow = |
| AddCSSImportant(GetEdgeEffectString(edge_effect)); |
| } |
| |
| if (font_color != CC::ClosedCaptionColor_Default) { |
| caption_style.text_color = |
| AddCSSImportant(GetCssColorWithAlpha(font_color, font_opacity)); |
| } |
| |
| if (background_color != CC::ClosedCaptionColor_Default) { |
| caption_style.background_color = AddCSSImportant( |
| GetCssColorWithAlpha(background_color, background_opacity)); |
| } |
| |
| if (region_color != CC::ClosedCaptionColor_Default) { |
| caption_style.window_color = |
| AddCSSImportant(GetCssColorWithAlpha(region_color, region_opacity)); |
| } |
| |
| return caption_style; |
| } |
| |
| } // namespace |
| |
| absl::optional<CaptionStyle> CaptionStyle::FromSystemSettings() { |
| if (base::win::GetVersion() >= base::win::Version::WIN10 && |
| base::FeatureList::IsEnabled(features::kSystemCaptionStyle)) { |
| return InitializeFromSystemSettings(); |
| } |
| // Return default CaptionStyle for pre Win10 versions since system settings |
| // don't allow caption styling. |
| return absl::nullopt; |
| } |
| |
| } // namespace ui |