caption_style_mac.mm 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. #include <AppKit/AppKit.h>
  5. #include <MediaAccessibility/MediaAccessibility.h>
  6. #include "base/mac/foundation_util.h"
  7. #include "base/mac/scoped_cftyperef.h"
  8. #include "base/mac/scoped_nsobject.h"
  9. #include "base/strings/stringprintf.h"
  10. #include "base/strings/sys_string_conversions.h"
  11. #include "skia/ext/skia_utils_mac.h"
  12. #include "third_party/skia/include/core/SkColor.h"
  13. #include "ui/base/ui_base_features.h"
  14. #include "ui/gfx/color_utils.h"
  15. #include "ui/native_theme/caption_style.h"
  16. namespace ui {
  17. namespace {
  18. constexpr auto kUserDomain = kMACaptionAppearanceDomainUser;
  19. // Adds !important to captions styles which are marked as important. They should
  20. // override any styles added by the video author or by a user stylesheet.
  21. std::string MaybeAddCSSImportant(std::string css_string, bool important) {
  22. std::string important_string = important ? " !important" : "";
  23. return css_string + important_string;
  24. }
  25. // Each of these functions returns a MediaAccessibility (MA) default value as a
  26. // CSS specifier string whose format is appropriate to the property. Some notes
  27. // about these:
  28. // 1) The behavior of MA properties (applies only as a default vs mandatory)
  29. // is ignored deliberately at the moment; there's no good way to express
  30. // this tri-state behavior in CSS and CaptionStyle doesn't support it on
  31. // other platforms. All the MA properties are treated as defaults here.
  32. // 2) The MA "window" concept is not implemented at all; it doesn't exist in
  33. // CaptionStyle or in the Blink-side WebVTT implementation.
  34. // 3) If a function is confused about how to map the system value to a Blink
  35. // WebVTT attribute (or a CSS specifier), it's always safe to return an
  36. // empty string, which will cause Blink to fall back to its default
  37. // behavior for that attribute.
  38. // 4) The only useful domain to retrieve attributes from is kUserDomain; the
  39. // system domain's values never change.
  40. std::string GetMAForegroundColorAndOpacityAsCSSColor() {
  41. MACaptionAppearanceBehavior behavior;
  42. base::ScopedCFTypeRef<CGColorRef> cg_color(
  43. MACaptionAppearanceCopyForegroundColor(kUserDomain, &behavior));
  44. bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  45. float opacity =
  46. MACaptionAppearanceGetForegroundOpacity(kUserDomain, &behavior);
  47. important |= (behavior == kMACaptionAppearanceBehaviorUseValue);
  48. SkColor rgba_color =
  49. SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity);
  50. return MaybeAddCSSImportant(color_utils::SkColorToRgbaString(rgba_color),
  51. important);
  52. }
  53. std::string GetMABackgroundColorAndOpacityAsCSSColor() {
  54. MACaptionAppearanceBehavior behavior;
  55. base::ScopedCFTypeRef<CGColorRef> cg_color(
  56. MACaptionAppearanceCopyBackgroundColor(kUserDomain, &behavior));
  57. bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  58. float opacity =
  59. MACaptionAppearanceGetBackgroundOpacity(kUserDomain, &behavior);
  60. important |= (behavior == kMACaptionAppearanceBehaviorUseValue);
  61. SkColor rgba_color =
  62. SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity);
  63. return MaybeAddCSSImportant(color_utils::SkColorToRgbaString(rgba_color),
  64. important);
  65. }
  66. // The MA text scale is a float between 0.0 and 2.0; this function converts it
  67. // to a CSS string specifying a percentage to scale the text by.
  68. std::string GetMATextScaleAsCSSPercent() {
  69. MACaptionAppearanceBehavior behavior;
  70. int percent =
  71. 100 * MACaptionAppearanceGetRelativeCharacterSize(kUserDomain, &behavior);
  72. bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  73. return MaybeAddCSSImportant(base::StringPrintf("%d%%", percent), important);
  74. }
  75. std::string GetMATextEdgeStyleAsCSSShadow() {
  76. MACaptionAppearanceBehavior behavior;
  77. MACaptionAppearanceTextEdgeStyle style =
  78. MACaptionAppearanceGetTextEdgeStyle(kUserDomain, &behavior);
  79. bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  80. // The MACaptionAppearanceTextEdgeStyle -> CSS shadow specs in this function
  81. // were found by experimentation and eyeballing - there's no known
  82. // documentation of how the MA shadow constants are supposed to look.
  83. switch (style) {
  84. case kMACaptionAppearanceTextEdgeStyleUndefined:
  85. case kMACaptionAppearanceTextEdgeStyleNone:
  86. // It's not clear when Undefined can be returned - here it's simply
  87. // treated as a synonym for None.
  88. return "";
  89. case kMACaptionAppearanceTextEdgeStyleRaised:
  90. return MaybeAddCSSImportant("-2px -2px 4px rgba(0, 0, 0, 0.5)",
  91. important);
  92. case kMACaptionAppearanceTextEdgeStyleDepressed:
  93. return MaybeAddCSSImportant("2px 2px 4px rgba(0, 0, 0, 0.5)", important);
  94. case kMACaptionAppearanceTextEdgeStyleUniform:
  95. // This system style looks like a 2px black stroke drawn around the edge
  96. // of the text. This isn't doable using CSS shadows, but drawing black
  97. // shadows around all the edges of the text produces a reasonable
  98. // imitation.
  99. return MaybeAddCSSImportant("-1px 0px 0px black, 0px -1px 0px black, "
  100. " 1px 0px 0px black, 0px 1px 0px black",
  101. important);
  102. case kMACaptionAppearanceTextEdgeStyleDropShadow:
  103. // Compose a pair of shadows to create the "drop" effect - a
  104. // semi-transparent shadow around the text, then a darker shadow below and
  105. // to the right of it.
  106. return MaybeAddCSSImportant(
  107. "0px 0px 2px rgba(0, 0, 0, 0.5), 2px 2px 2px black", important);
  108. }
  109. }
  110. // Thankfully, CSS font descriptors can simply use font names understood by the
  111. // platform, so that's what this function does. It only returns attributes for
  112. // the default font. The system allows configuring default font variants for
  113. // each font face to be used in WebVTT captions, which is not implemented here.
  114. void GetMAFontAsCSSFontSpecifiers(std::string* font_family,
  115. std::string* font_variant) {
  116. base::ScopedCFTypeRef<CTFontDescriptorRef> ct_font_desc(
  117. MACaptionAppearanceCopyFontDescriptorForStyle(
  118. kUserDomain, nullptr, kMACaptionAppearanceFontStyleDefault));
  119. base::ScopedCFTypeRef<CFStringRef> ct_font_family_name(
  120. base::mac::CFCast<CFStringRef>(CTFontDescriptorCopyAttribute(
  121. ct_font_desc, kCTFontFamilyNameAttribute)));
  122. if (ct_font_family_name)
  123. *font_family = base::SysCFStringRefToUTF8(ct_font_family_name);
  124. base::ScopedCFTypeRef<CFStringRef> ct_font_face_name(
  125. base::mac::CFCast<CFStringRef>(
  126. CTFontDescriptorCopyAttribute(ct_font_desc, kCTFontNameAttribute)));
  127. if (ct_font_face_name)
  128. *font_variant = base::SysCFStringRefToUTF8(ct_font_face_name);
  129. }
  130. std::string GetMAWindowColorAsCSSColor() {
  131. MACaptionAppearanceBehavior behavior;
  132. base::ScopedCFTypeRef<CGColorRef> cg_color(
  133. MACaptionAppearanceCopyWindowColor(kUserDomain, &behavior));
  134. bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  135. float opacity = MACaptionAppearanceGetWindowOpacity(kUserDomain, &behavior);
  136. important |= (behavior == kMACaptionAppearanceBehaviorUseValue);
  137. SkColor rgba_color =
  138. SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity);
  139. return MaybeAddCSSImportant(color_utils::SkColorToRgbaString(rgba_color),
  140. important);
  141. }
  142. std::string GetMAWindowRadiusAsCSSNumberInPixels() {
  143. MACaptionAppearanceBehavior behavior;
  144. float radius =
  145. MACaptionAppearanceGetWindowRoundedCornerRadius(kUserDomain, &behavior);
  146. bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  147. return MaybeAddCSSImportant(base::StringPrintf("%fpx", radius), important);
  148. }
  149. } // namespace
  150. // static
  151. absl::optional<CaptionStyle> CaptionStyle::FromSystemSettings() {
  152. if (!base::FeatureList::IsEnabled(features::kSystemCaptionStyle))
  153. return absl::nullopt;
  154. CaptionStyle style;
  155. style.text_color = GetMAForegroundColorAndOpacityAsCSSColor();
  156. style.background_color = GetMABackgroundColorAndOpacityAsCSSColor();
  157. style.text_size = GetMATextScaleAsCSSPercent();
  158. style.text_shadow = GetMATextEdgeStyleAsCSSShadow();
  159. style.window_color = GetMAWindowColorAsCSSColor();
  160. style.window_radius = GetMAWindowRadiusAsCSSNumberInPixels();
  161. GetMAFontAsCSSFontSpecifiers(&style.font_family, &style.font_variant);
  162. return style;
  163. }
  164. } // namespace ui