File: caption_style_mac.mm

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (189 lines) | stat: -rw-r--r-- 8,500 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright 2019 The Chromium Authors
// 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 <AppKit/AppKit.h>
#include <MediaAccessibility/MediaAccessibility.h>

#include "base/apple/foundation_util.h"
#include "base/apple/scoped_cftyperef.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "skia/ext/skia_utils_mac.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/color_utils.h"

namespace ui {

namespace {

constexpr auto kUserDomain = kMACaptionAppearanceDomainUser;

// Adds !important to captions styles which are marked as important. They should
// override any styles added by the video author or by a user stylesheet.
std::string MaybeAddCSSImportant(std::string css_string, bool important) {
  std::string important_string = important ? " !important" : "";
  return css_string + important_string;
}

// Each of these functions returns a MediaAccessibility (MA) default value as a
// CSS specifier string whose format is appropriate to the property. Some notes
// about these:
//   1) The behavior of MA properties (applies only as a default vs mandatory)
//      is ignored deliberately at the moment; there's no good way to express
//      this tri-state behavior in CSS and CaptionStyle doesn't support it on
//      other platforms. All the MA properties are treated as defaults here.
//   2) The MA "window" concept is not implemented at all; it doesn't exist in
//      CaptionStyle or in the Blink-side WebVTT implementation.
//   3) If a function is confused about how to map the system value to a Blink
//      WebVTT attribute (or a CSS specifier), it's always safe to return an
//      empty string, which will cause Blink to fall back to its default
//      behavior for that attribute.
//   4) The only useful domain to retrieve attributes from is kUserDomain; the
//      system domain's values never change.

std::string GetMAForegroundColorAndOpacityAsCSSColor() {
  MACaptionAppearanceBehavior behavior;
  base::apple::ScopedCFTypeRef<CGColorRef> cg_color(
      MACaptionAppearanceCopyForegroundColor(kUserDomain, &behavior));
  bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  float opacity =
      MACaptionAppearanceGetForegroundOpacity(kUserDomain, &behavior);
  important |= (behavior == kMACaptionAppearanceBehaviorUseValue);

  SkColor rgba_color =
      SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity);
  return MaybeAddCSSImportant(color_utils::SkColorToRgbaString(rgba_color),
                              important);
}

std::string GetMABackgroundColorAndOpacityAsCSSColor() {
  MACaptionAppearanceBehavior behavior;
  base::apple::ScopedCFTypeRef<CGColorRef> cg_color(
      MACaptionAppearanceCopyBackgroundColor(kUserDomain, &behavior));
  bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  float opacity =
      MACaptionAppearanceGetBackgroundOpacity(kUserDomain, &behavior);
  important |= (behavior == kMACaptionAppearanceBehaviorUseValue);

  SkColor rgba_color =
      SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity);
  return MaybeAddCSSImportant(color_utils::SkColorToRgbaString(rgba_color),
                              important);
}

// The MA text scale is a float between 0.0 and 2.0; this function converts it
// to a CSS string specifying a percentage to scale the text by.
std::string GetMATextScaleAsCSSPercent() {
  MACaptionAppearanceBehavior behavior;
  int percent =
      100 * MACaptionAppearanceGetRelativeCharacterSize(kUserDomain, &behavior);
  bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  return MaybeAddCSSImportant(base::StringPrintf("%d%%", percent), important);
}

std::string GetMATextEdgeStyleAsCSSShadow() {
  MACaptionAppearanceBehavior behavior;
  MACaptionAppearanceTextEdgeStyle style =
      MACaptionAppearanceGetTextEdgeStyle(kUserDomain, &behavior);
  bool important = behavior == kMACaptionAppearanceBehaviorUseValue;

  // The MACaptionAppearanceTextEdgeStyle -> CSS shadow specs in this function
  // were found by experimentation and eyeballing - there's no known
  // documentation of how the MA shadow constants are supposed to look.
  switch (style) {
    case kMACaptionAppearanceTextEdgeStyleUndefined:
    case kMACaptionAppearanceTextEdgeStyleNone:
      // It's not clear when Undefined can be returned - here it's simply
      // treated as a synonym for None.
      return "";
    case kMACaptionAppearanceTextEdgeStyleRaised:
      return MaybeAddCSSImportant("-2px -2px 4px rgba(0, 0, 0, 0.5)",
                                  important);
    case kMACaptionAppearanceTextEdgeStyleDepressed:
      return MaybeAddCSSImportant("2px 2px 4px rgba(0, 0, 0, 0.5)", important);
    case kMACaptionAppearanceTextEdgeStyleUniform:
      // This system style looks like a 2px black stroke drawn around the edge
      // of the text. This isn't doable using CSS shadows, but drawing black
      // shadows around all the edges of the text produces a reasonable
      // imitation.
      return MaybeAddCSSImportant("-1px 0px 0px black, 0px -1px 0px black, "
                                  " 1px 0px 0px black, 0px  1px 0px black",
                                  important);
    case kMACaptionAppearanceTextEdgeStyleDropShadow:
      // Compose a pair of shadows to create the "drop" effect - a
      // semi-transparent shadow around the text, then a darker shadow below and
      // to the right of it.
      return MaybeAddCSSImportant(
          "0px 0px 2px rgba(0, 0, 0, 0.5), 2px 2px 2px black", important);
  }
}

// Thankfully, CSS font descriptors can simply use font names understood by the
// platform, so that's what this function does. It only returns attributes for
// the default font. The system allows configuring default font variants for
// each font face to be used in WebVTT captions, which is not implemented here.
void GetMAFontAsCSSFontSpecifiers(std::string* font_family,
                                  std::string* font_variant) {
  base::apple::ScopedCFTypeRef<CTFontDescriptorRef> ct_font_desc(
      MACaptionAppearanceCopyFontDescriptorForStyle(
          kUserDomain, nullptr, kMACaptionAppearanceFontStyleDefault));

  base::apple::ScopedCFTypeRef<CFStringRef> ct_font_family_name(
      base::apple::CFCast<CFStringRef>(CTFontDescriptorCopyAttribute(
          ct_font_desc.get(), kCTFontFamilyNameAttribute)));
  if (ct_font_family_name) {
    *font_family = base::SysCFStringRefToUTF8(ct_font_family_name.get());
  }

  base::apple::ScopedCFTypeRef<CFStringRef> ct_font_face_name(
      base::apple::CFCast<CFStringRef>(CTFontDescriptorCopyAttribute(
          ct_font_desc.get(), kCTFontNameAttribute)));
  if (ct_font_face_name) {
    *font_variant = base::SysCFStringRefToUTF8(ct_font_face_name.get());
  }
}

std::string GetMAWindowColorAsCSSColor() {
  MACaptionAppearanceBehavior behavior;
  base::apple::ScopedCFTypeRef<CGColorRef> cg_color(
      MACaptionAppearanceCopyWindowColor(kUserDomain, &behavior));
  bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  float opacity = MACaptionAppearanceGetWindowOpacity(kUserDomain, &behavior);
  important |= (behavior == kMACaptionAppearanceBehaviorUseValue);

  SkColor rgba_color =
      SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity);
  return MaybeAddCSSImportant(color_utils::SkColorToRgbaString(rgba_color),
                              important);
}

std::string GetMAWindowRadiusAsCSSNumberInPixels() {
  MACaptionAppearanceBehavior behavior;
  float radius =
      MACaptionAppearanceGetWindowRoundedCornerRadius(kUserDomain, &behavior);
  bool important = behavior == kMACaptionAppearanceBehaviorUseValue;
  return MaybeAddCSSImportant(base::StringPrintf("%fpx", radius), important);
}

}  // namespace

// static
std::optional<CaptionStyle> CaptionStyle::FromSystemSettings() {
  CaptionStyle style;

  style.text_color = GetMAForegroundColorAndOpacityAsCSSColor();
  style.background_color = GetMABackgroundColorAndOpacityAsCSSColor();
  style.text_size = GetMATextScaleAsCSSPercent();
  style.text_shadow = GetMATextEdgeStyleAsCSSShadow();
  style.window_color = GetMAWindowColorAsCSSColor();
  style.window_radius = GetMAWindowRadiusAsCSSNumberInPixels();

  GetMAFontAsCSSFontSpecifiers(&style.font_family, &style.font_variant);

  return style;
}

}  // namespace ui