【问题标题】:Rendering font character LSB or placement varies渲染字体字符 LSB 或位置不同
【发布时间】:2020-09-21 08:07:04
【问题描述】:

在某些情况下,我遇到了字形间距不一致的问题。

我使用 DirectWrite 和 IDWriteFontFace 并使用 GetGlyphIndicesGetDesignGlyphMetrics 来获取字形的指标,这样我就可以获取每个字形,将其存储为图像,然后使用字形重建整个字符串信息(提前,LSB,RSB等)。我使用带有 WIC 位图的 ID2D1RenderTarget.DrawTextLayout 渲染每个字形。虽然我所做的 99% 的测试似乎都有效,但我遇到了不一致的间距,我不知道是什么原因造成的。也许有人可以解释一下,但我不知道从哪里开始。

从我的基本解释来看,字体布局是由将字形渲染为 lsb + 宽度组成,然后放置下一个字符的位置称为提前,提前宽度由 lsb、字形宽度、rsb 组成。


我正在使用这种 Zapfino 字体,因为它看起来相当复杂,并且是测试事物是否正确呈现的一个很好的指标。

所以这里逐行进行一些渲染对比:

  1. 正确渲染:这里是左右两个不同的字符串,通过paint.net正确渲染

  2. 我的渲染:我包括提前展示位置(红色)和起始位置(黄色)。 在左边,我们有世界!渲染,和世界!已正确放置,但与正确的渲染相比,W 和 o 之间的距离现在太大了。

    在右侧,我们有 Hold!渲染并且大部分与paint.net渲染一致。

  3. 我将两者重叠以便更好地进行比较。我对齐orld的左边!所以你可以看到至少那部分是一致的,它是 W 关闭。右侧几乎相同。

  4. Paint.net 显示光标位置在我认为是前进的位置。您可以看到提前放置,o 就在它的顶部。但是右边有o,你可以看到它从advance到o有距离。当在前面 (5)、不同字符旁边或单独渲染 o 时,它仍然具有该距离。我相信(外观方面)似乎是 LSB 缩小了?

  5. 天哪!使用带有光标的paint.net 渲染。你可以看到 o 与前进相交。其他一些例子,We 和 Wo 有同样的问题,缩小 LSB,但例如在 Wi 中,字形与前进有适当的距离,就像它自己一样。

根据字符缩小 LSB 或移动字形位置的这种效果称为什么?如何通过字形数据检测此类行为?

【问题讨论】:

  • 您是否一次处理一个字符,即获取每个字符的字形和指标,然后将每个字符绘制到渲染目标?如果是这样,那不是你应该做的。
  • 是的,目前我需要分别渲染每个字形。为此,我采用渲染的字形并根据字形和布局指标对其进行排列。
  • 逐个字形渲染会显着降低功能。例如,您的示例图像显示了一个脚本/书法字体,其中一些字母对之间存在连接。该字体可能具有替代字形,可根据相邻字形提供适当的连接笔划。但是,如果您一次处理一个字形,您将不会获得上下文替换。 OpenType 和 DirectWrite 真正适用于渲染 runs 文本。
  • 我意识到我将不得不牺牲一些功能。不幸的是,对于我正在使用的用例,我们必须将每个字形分离成它自己的 BGRA 数据,以便在 OpenGL 中用作纹理。否则,每个文本绘制都将被视为一个单独的纹理/绘制调用,这将使其无法使用。使用这种方法,我们可以在不牺牲性能的情况下进行文本渲染。我一直在寻找方法来扩展它以获得一些常见的功能,例如字距调整。 TryGetFontTable 至少应该让我更接近。

标签: fonts rendering directwrite


【解决方案1】:

Shaping 会自动处理 GPOS 字距调整,您无需直接访问值。布局显然要经过整形,以这种方式应用字距调整。 IDWriteTextLayout1 接口允许为每个范围启用对字距调整,但这可能仅在没有可用的字距调整 GPOS 功能时使用。

基本上,使用简单的设计改进和标称字形进行渲染仅用于演示目的,其他一切都应经过整形过程,无论是使用布局 API 还是通过 GetGlyphs()/GetGlyphPlacements() 手动进行。

【讨论】:

    【解决方案2】:

    我想通了,是字距调整。我被甩了,因为当我检查字体时,DirectWrite 为HasKerningPairs 返回 0。但是,我在字体编辑器中打开了字体,可以看到它确实有字距调整对。进一步查看它说的文档:

    Newer fonts may have only GPOS kerning instead of the legacy pair-table kerning. Such fonts, like Gabriola, will only return 0's for adjustments. GetKerningPairAdjustments doesn't virtualize and flatten these GPOS entries into kerning pairs.

    他们没有提供任何其他方式来抓住它们,所以这是一条死胡同。无法从 DirectWrite 中的字体中获取这些变量,我觉得这很奇怪,因为内置的 TextLayout 在内部进行了这些调整。但是,由于DirectWrite提供的API没有解决这个问题,可以标记为已解决和未解决。

    【讨论】:

    • 自己处理 GSUB 和 GPOS 表非常重要。如果你真的想这样做,start here to learn details。您可以使用IDWriteFontFace::TryGetFontTable() 获取要处理的原始表。
    • “内置 TextLayout 在内部进行这些调整” - IDWriteTextLayout 只是将所有 GPOS 工作推迟到 GetGlyphs() 调用,因为它对 GPOS 本身一无所知。
    • GPOS 在TextAnalyzer 中处理GetGlyphs 的哪一部分?从外观上看,客户端实现了很多东西。只是想知道我是否必须在客户端某处实现对 opentype 表的整个解析才能做到这一点,或者我是否可以依靠 TextAnalyzer 来处理它?
    猜你喜欢
    • 1970-01-01
    • 2015-01-27
    • 2015-09-03
    • 1970-01-01
    • 1970-01-01
    • 2018-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多