使用Earcut从OpenType.js中三角剖分路径数据

3
我有一个使用情况,需要在画布元素上呈现大量(~50,000字形)清晰、可伸缩的文本字符串。到目前为止,我尝试过的最佳解决方案涉及将绘制在画布元素上的文本三角化(文本是使用fillText方法绘制的),上传矩阵统一和表示该字符串的三角形的Float32Array到WebGL中的GPU。使用此方法,我能够以约30fps渲染100,000个字形。在非常高的缩放级别下,字形变得模糊,但对于我的用例来说这是可以接受的。
然而,由于我首先将字符串绘制到内存中的画布元素上,读取像素数据,将位图图像转换为矢量并三角化矢量数据,因此此方法的开销约为每个字符串250ms左右。在网上搜索解决方案时,我发现了两个有趣的开源项目: 现在我想重新编写我的概念验证,使用OpenType和Earcut。 OpenType用于将曲线数据输入到Earcut中,而Earcut用于三角剖分该数据并返回表示每个三角形点的数组。 我的问题是,我无法弄清楚如何获取OpenType提供的数据并将其转换为Earcut接受的格式。有人可以提供帮助吗? 更多信息: 这个StackOverflow问题提供了一些很好的信息,但缺少一些实现细节:WebGL中更好的文本质量。我想我试图实现的是第一个答案中描述的“字体作为几何体”的方法。

这听起来像是一个XY问题;为什么你需要在单个画布上同时渲染那么多字形? - Mike 'Pomax' Kamermans
感谢您的评论。我的用例是一个项目管理Web应用程序,其中每个任务在最大缩放级别下可以有五个不同的字符串,并且用户可以在日程表周围平移/缩放。已经进行了修剪,因此屏幕外的字形不会呈现出来,以及一种详细级别系统,因此在缩放时会呈现更多细节。但是,在相对较低的缩放级别下,文本显示为任务描述的一行可见。通常一次可见1,000个以上的任务。 - Synthesize
我认为问题是 - 你真的需要WebGL吗?常规的2D画布使用相同的硬件加速,并且具有更复杂的算法,可以在不必将曲线分解成段并进行三角剖分的情况下填充轮廓。 - riv
我的第一次尝试是使用两个画布元素。一个带有WebGL上下文,另一个带有CanvasRenderingContext2D。所有文本都是在CanvasRenderingContext2D上绘制的,然后覆盖在主GL上下文画布上。这在Chrome和iOS Safari上效果很好,但是在Firefox和Edge上绘制文本速度明显较慢(使用5,000作为测试用例),这对我们来说是不可行的。 - Synthesize
1个回答

6
您可以使用Font.getPath创建路径。路径由move-to、line-to、curve-to、quad-to和close指令组成,可通过path.commands访问。当然,您需要先将bezier曲线指令转换为小段。

一旦您有了一组封闭路径,您需要确定哪些是洞。内部轮廓的方向与外部轮廓相反,并且您可以将它们分配给包含它们的最小外部轮廓。一旦您拥有了<外部轮廓和一组洞>,就可以将其提供给earcut库。

这是一个简单的实现,假设没有交集。对于大多数字体,对我来说效果非常好,除了极少数具有相交路径的“花哨”字体。

以下是一个工作示例:https://jsbin.com/gecakub/edit?html,js,output

您可以为每个字符串而不是为每个网格创建网格,然后使用库中的字距数据自行定位它们。

编辑:此解决方案仅适用于TTF字体,但可以通过忽略路径方向并使用更好的“路径A在路径B内”检查进行CCF ( .otf )进行简单调整,除非字体具有相交路径。


谢谢!这是一个很棒的答案,正是我在寻找的。 - Synthesize
在OpenType字体中,CFF轮廓使用交叉数来确定孔洞,而TTF轮廓使用路径方向来确定孔洞,SVG轮廓则根据每个字形“任选其一,只需适当设置fillrule”。因此,我不知道OpenType.js是否忽略这些差异并重新编写路径,还是尊重原始字形类型。但如果您知道其中的哪一个,更新解释如何确定哪些子路径可能表示孔洞的部分将是值得的。 - Mike 'Pomax' Kamermans
啊,这是个好观点。我只使用了TTF字体,不知道库是否对其他字体类型进行任何处理(在我的原始项目中,我使用了FreeType,所以我对OpenType.js的了解更少)。这个解决方案肯定会在轮廓线方向无关紧要的情况下出现问题,但可以通过用更健壮的代码替换方向检查来修复它,以检查路径X是否在路径Y内(尽管无法处理自相交)。 - riv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接