每个后端上只有一条字形管线,而且它是 MSDF。字形以固定的 4 像素范围栅格化为
多通道有符号距离场,并打包进引擎自己的图集,所以
Scale 是一个自由参数而不是质量设置,
也永远不会为每个字号重建图集。
字体在启动期间注册一次。多个字体可以同时驻留,字形度量 按字体和码点缓存。
var font = await Season.Fonts.Font.CreateAsync("Fonts/NotoSans-Regular.ttf", 48f);
size 参数是栅格化尺寸,它影响图集
占用和最小可读细节 — 而不是文本渲染时的大小。那个是控件上的
Scale,而且它是独立的。
title = new Texts
{
Content = "Settings",
Color = Colors.DarkRed,
Scale = Vector2.One * 1.2f,
OnClick = () => settingPanel.Alpha = 1f
};
AddControl(title);
body = new Texts
{
Content = longParagraph,
WidthRequest = 360, // wrapping box; null means one line
LineHeight = 34
};
AddControl(body);
| 成员 | 说明 |
|---|---|
Content | 字符串。赋值会触发布局。ContentOrigin 保留未翻译的原文。 |
Translate | 默认 true:字符串在布局前经过本地化。对名字、数字和日志行设为 false。 |
Scale | 一个 Vector2,所以文本可以在一个轴上拉伸。不损失锐度。 |
LineHeight | 以设计单位计的行进。默认 40。 |
WidthRequest、HeightRequest | 换行盒。两者都留 null,文本就是单行。 |
WordsSpace、EmptySpace | 额外的字距,以及拉丁段落中一个空格的宽度。EmptySpace 默认 10。 |
OriginWidth、OriginHeight | 在布局后读回测量尺寸,用于在其旁边摆放东西。 |
TextsType | Immediately 或 FadeIn,用于让文本渐显而不是弹出。 |
Append(string) | 追加一个片段并只布局尾部。见下文。 |
Translate 默认开启
每次 Content 赋值都会在布局前被查找。对 UI 标签
这正是你想要的,而对玩家名字或文件路径则绝对不是,所以在那些控件上
于赋值前设置 Translate = false。
Content 内部理解一小套 span 标记,
足以高亮一个词而不必把一个段落拆成三个控件。
log.Content = "Build <span style='color:Red;font-weight:bold;'>failed</span> in 2.4s";
这就是全部标记面。没有富文本文档模型,没有样式表,也没有 数据绑定 — 这是刻意的,因为一旦文本控件接受一个文档,你就拥有 一个布局引擎。
Append 存在,是因为 token 流存在。它只针对
当前行开始处布局状态的检查点布局新的尾部,所以
增量追加与完全重排逐像素一致 — 包括整词前瞻,
这是朴素的尾部追加会做错的部分。
// A model streams tokens; the paragraph is not rebuilt sixty times a second.
await foreach (var token in stream)
answer.Append(token);
颜色作用域可以跨块边界,而 span 索引在整个字符串内是绝对的,
所以尾部无法单独解析。一旦 Content 中出现过
span 标记,追加就回退到完全重排,直到下一次整体内容赋值。
流式与行内颜色各自都支持;两者结合,你就得为颜色付出代价。
断行是脚本感知的,而不是基于字符计数。CJK 在字符之间断行, 包括位于主统一区之外、且不能作为行首的标点; 拉丁文按整词断行,并在确认换行前前瞻测量该词。
两条规则在同一遍中运行,所以中英混排的段落无需你选择模式就能正确换行。 引擎不做的是连字、双向文本,以及印度系和阿拉伯系文字的复杂塑形。
Overlay 文本在色调映射之后合成,并施加了反向 ACES 补偿,所以
你要求的颜色就是屏幕上的颜色,而不是它被色调映射后的版本。这也是
为什么留在 RenderDomain.Scene 里的 HUD 看起来不对:它
会和世界一起被分级、泛光和 TAA 处理,而细字形边缘正是
时间累积会涂抹的东西。