Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Glyph dilation algorithm using the same macOS formula #819

Open
m13253 opened this issue Oct 24, 2021 · 6 comments
Open

Comments

@m13253
Copy link

m13253 commented Oct 24, 2021

MacOS, by default, emboldens each glyph slightly to mimic ink dilation on paper. Therefore, each glyph looks slightly bolder than the metal design, as if it is printed on paper.

Meanwhile, Windows does not do glyph dilation. Some Windows fonts, such as Courier New, which is digitized based on the metal shape instead of the real appearance on paper, relies on TrueType hinting to embolden the stroke when displayed on screen. Other fonts don't embolden this much, but more or less it uses hinting to match the darkness as macOS.

Currently, MacType uses a shadow to increase the contrast and make the font look darker. However this is done after rasterization, after the pixel boundaries are fixed. It would be better if the glyph dilation happens on the curve, even before the rasterization. I believe there are such API in FreeType since it supports outline synthesizing.

According to Patrick Walton, who is one of the leading developers of Pathtracer, figured out the macOS dilation formula:

radius = min(vec2(0.3px), vec2(0.015125, 0.0121) * S)

where S is the font size in px. (Be careful of HiDPI.)

Patrick claims that he can achieve a pixel-by-pixel identical render as macOS using Rusttype. This proves his formula has a high chance of being correct.

See also:

  • Hacker News: https://news.ycombinator.com/item?id=23553486
    Linux users complain that new web fonts have a trend to be thinner than normal, due to the glyph dilation algorithm used in macOS. This is also because FreeType uses integration method similar to macOS, instead of the worse supersampling method similar to Windows.
  • 《油墨陷阱和它的朋友们》:https://www.thetype.com/2021/06/21723/
    This article in Chinese, explains the ink dilation phenomenon and various solutions.

MacOS 默认会对字体的笔画进行轻微的扩散,用来模拟油墨扩散效果。这样看起来,文字会相比铅字设计稍微变粗,更接近印在纸上的效果。

然而,Windows 并不进行字体扩散。虽然有一部分 Windows 字体,比如 Courier New,依赖 TrueType hinting 来在显示器上加粗笔画:这是因为 Courier New 的数字化建模是基于金属字模而不是纸张上实际的样子的。别的字体并不会加粗这么多,但或多或少使用 hinting 来让文字的深浅匹配 macOS。

目前,MacType 使用阴影来提升对比度并加深文字。然而阴影是在栅格化之后,像素的边界已经写下来之后才进行的。如果能直接在曲线上进行轮廓扩散,或者说在栅格化之前进行,效果应当会更好。我认为 FreeType 应该有轮廓合成的 API 接口。

Patrick Walton,是 Pathtracer 的主要开发者之一。他发现了 macOS 字体扩散公式

radius = min(vec2(0.3px), vec2(0.015125, 0.0121) * S)

其中 S 是字体尺寸,单位是像素。(注意高分屏的情况。)

Patrick 声称他能用 Rusttype 实现与 macOS 逐像素对比一致的渲染。这说明他的公式有很大可能性是正确的。

参见:

  • Hacker News: https://news.ycombinator.com/item?id=23553486
    Linux 用户抱怨现在流行的网页字体比实际上更细,可能是因为 macOS 的字体扩张算法。另一方面,FreeType 使用类似 macOS 的数值积分法来渲染文字,而不是类似 Windows 稍逊色的超采样法。
  • 《油墨陷阱和它的朋友们》:https://www.thetype.com/2021/06/21723/
    这篇文章解释了油墨稀释现象与字体设计师的应对措施。

Screenshots of Pathfinder rendering glyphs in Firefox
Screenshots of Pathfinder rendering glyphs in Firefox.
使用 Pathfinder 引擎在 Firefox 中渲染文字。
(Image source: https://twitter.com/pcwalton/status/971475785616797698)

Font render differences between Windows and macOS
According to my experiment, macOS actually uses a wrong gamma value (1.0 vs 2.2), which makes glyph even more darker than designed. (Comparing to native Windows renderer, which mistakenly mixes different gamma values ranging from 1.x to 4.x, causing different appearances across different applications even for the same font.)
根据我自己的实验,macOS 使用了错误的 gamma 值(1.0 而非 2.2),导致字体颜色更深。(相比之下原生 Windows 混用从 1.x 到 4.x 不等的 gamma 值,所以不同程序里同一个字体渲染效果常常不一样。)
(Image source: https://twitter.com/m13253/status/1408177729665744911)

@m13253 m13253 changed the title [Feature Request] Glyph dilation algorithm as what macOS does [Feature Request] Glyph dilation algorithm using the same macOS formula Oct 24, 2021
@ghost
Copy link

ghost commented Oct 25, 2021

老铁的issue真专业。分析的有理有据,令人信服。

@snowie2000
Copy link
Owner

Very compelling analysis. MacType uses FT_Outline_EmboldenXY to embolden glyphs which indeed transforms glyph outlines before they are rasterized, not in the way of using a shadow.

MacType shadow is a way to "decorate" your font display instead of making them look bold. It gives you a sense of depth in your texts.

The formula you have given of "smart" scaling is really the key. MacType now depends on users to specify boldness for fonts (or maybe every individual font) to embolden it. It can't automatically determine how hard it should embolden different fonts in different sizes, and your formula gives it a way out.

PS: What is vec2 in the equation? I don't know anything about rust, but I'll try to read his code.

@m13253
Copy link
Author

m13253 commented Oct 25, 2021

PS: What is vec2 in the equation? I don't know anything about rust, but I'll try to read his code.

I believe vec2 means X and Y direction. But I don't know which number is X, which number is Y.

@m13253
Copy link
Author

m13253 commented Oct 25, 2021

In detail, the code could be:

radius_x = 0.015125 * S;
radius_y = 0.0121 * S;
if (radius_x > 0.3px) { radius_x = 0.3px; }
if (radius_y > 0.3px) { radius_y = 0.3px; }

or, the other way around:

radius_x = 0.0121 * S;
radius_y = 0.015125 * S;
if (radius_x > 0.3px) { radius_x = 0.3px; }
if (radius_y > 0.3px) { radius_y = 0.3px; }

I don't know which way is correct. Should we ask Patrick Walton?
We could experiment on our own, either.

Update: Added the S < 72px condition. (This is not true.)

@m13253
Copy link
Author

m13253 commented Oct 25, 2021

The numbers also present in the code below:
https://github.com/servo/pathfinder/blob/a82bad90daf09d717bd0fb8b8960a11d85e4446a/content/src/effects.rs#L29-L40

Again, from the code I can't determine which number is X, which number is Y.
But that code shows that glyph dilation is turned off when ppem > 72px. (This is not true.)

@snowie2000
Copy link
Owner

It doesn't matter which is which, we can make it out by trial and error before we put it into effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants