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
|