-
-
Notifications
You must be signed in to change notification settings - Fork 789
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
Improve accuracy of CJK glyphs with higher resolution TinySDF textures #2990
Comments
Generally speaking, and this is only my personal opinion, increasing to 48 feels like a patch and not a real solution, I would prefer to have solution that really solves this problem, preferably even one that allows using regular font files instead of this pre-generated glyphs. |
@HarelM sure, I agree at a high level a solution that does not pre-rendered SDFs is better, for example:
But even if you have a non-pregenerated glyph solution, the rendering and WebGL components of MapLibre will always need to deal with signed distance fields. Any move away from SDFs would have a major impact on the user experience and styling capabilities of MapLibre. So an enhancement to the SDF pipeline for higher res textures is complementary to the solution you mentioned, not an alternative. Other hardware-accelerated text display systems like video games use SDFs, but they can load fonts on disk and have higher VRAM budgets, so are not subject to the same constraints. A reasonable alternative to this proposal is to keep 24pt fonts but use multi-channel SDFs: https://github.com/Chlumsky/msdfgen (MIT license) I think moving all of MapLibre to MSDFs is viable, but it would also require re-generating all font stacks, would triple the TinySDF cost and triple VRAM usage even if all glyph textures in the atlas are 24pt - you'd then need red, green and blue channels. |
This is a great write-up @bdon! The Mapbox GL JS equivalent of this is one of the few things I worked on post-fork, so I think I need to stay a little bit at arms-length from this, but just to share a few bits of perspective from that effort:
|
Thanks for this nice summary @bdon, I could not have written it better. Your proposal seems to be the right choice from my point of view. It is an incremental improvement of our text rendering stack and the risk of doing something wrong is very small. |
I've made an interactive comparison of my branch vs maplibre 3.3.0: https://bdon.github.io/maplibre-cjk/ Video: jp-demo.mp4It turns out this is easier than I expected to implement, without having to modify any shaders or vertex buffer layouts, only code that touches JavaScript. I haven't tested 100% of all cases or looked closely at the typographic metrics to make sure the spacing is the same between old and new code. |
Thanks for making a live demonstration. It is really eye-opening! |
I've updated the demo to align the new glyphs pixel-perfect with the old ones, so visual styling should remain the same with a minor version bump. (At least on Apple devices, need to test others) https://bdon.github.io/maplibre-cjk/ I've also fixed Another comparison showing a particularly bad case that is fixed now: cjk-pixel-align.mp4 |
Looks great! |
* Improve CJK text rendering [#2990] * Double TinySDF-rendered glyphs with new texScale glyph property * Change topAdjustment/leftAdjustment to make 2x glyphs match 1x positions * rename texScale to textureScale [#2990] * set textureScale to 1 for image SDFs [#2990] * Simplify implementation to an optional boolean [#2990] * resolve CHANGELOG.md conflict [#3006] * move comment [#3006] * set allowed diff to 0 for localIdeographs [#3006] * fix duplicate entry in CHANGELOG * Make local ideographs test font sizes much bigger [#3006] * add expected images for ubuntu/windows [#3006]
Released in 3.4.1 |
User Story
As a viewer of the map in Chinese or Japanese language, the quality of rendered text should look as good as Latin text and other scripts.
Rationale
The drawback to the SDF approach is that it isn't the format fonts are commonly stored in. Fonts are defined as a collection of Bézier curves in a .TTF or .OTF file. In order to display them as SDFs they need to be "baked" into a texture via preprocessing with a program like font-maker.
The conversion from .TTF to SDFs requires the choice of a specific font size for rasterization into a bitmap via FreeType. Early on in GL JS development this was determined to be a 24 point font as a compromise between text sharpness and SDF bitmap size. SDF bitmaps are then stored in ranges of 256 glyphs and served over HTTP. A "font stack" is 256 files of 256 glyphs each, covering the entire 65,536 codepoint Basic Multilingual Plane
SDF 24 point font is good enough to render most Latin fonts with a small amount of rounding. The issue arises when rendering glyphs with more internal detail. These are common where CJK is used with "traditional" scripts - mainly Japan, Taiwan, Hong Kong and Macau. From the MapLibre example page I screenshotted a few1:
Later on in GL JS development, it was decided to special case Chinese, Japanese and Korean glyphs to ignore the above code path, because the amount of network traffic required impacted map performance. Natural CJK text has poor locality over the space of UTF-8 codepoints, so a single map tile could result in several, even dozens of SDF files requested. The alternative
localIdeographs
code path renders text via mapbox/tinysdf (BSD) and can be controlled by the localIdeographFontFamily option.TinySDF skips over the FreeType pre-baking step by using the browser's Canvas API to draw text to a canvas, and then converts that canvas into an SDF. This reduces network requests for CJK glyphs to 0, but limits the display of CJK text to only the basic fonts built into the browser (Sans Serif for Gothic style, Serif for Ming/Song style)
TinySDF takes the 24 point font size as as parameter, this is hardcoded in GL JS to 24pt
Because TinySDF takes any font size as a parameter, we can increase the rendering resolution; most obvious would be doubling it to 48pt instead of 24. You can see the difference on the mapbox/TinySDF demo page (BSD).

so shaders like symbol_sdf.vertex.glsl need to be updated:edit: see below, no shader modifications necessary#1051
#1002
maplibre/maplibre-style-spec#174
Impact
Alternatives
Footnotes
Note the display of U+FA10 塚 may look unusual, this is my browser's locale setting and is not relevant to this issue. ↩
The text was updated successfully, but these errors were encountered: