Maximkaaa / galileo

General purpose cross-platform GIS-rendering library written in Rust
Apache License 2.0
365 stars 25 forks source link

Text rendering #48

Open Maximkaaa opened 7 months ago

Maximkaaa commented 7 months ago

To support labels on the map, we need a mechanism to render shaped text onto the map. This includes:

Rendering a text along a path would be desirable, but we can do it separately later.

At the first glance, https://github.com/grovesNL/glyphon library can cover all our needs. iced uses it for text shaping, so it must be mature enough to support our use cases.

We would also need test cases for complex cases:

Must have options for initial implemenation:

Maximkaaa commented 7 months ago

@maxammann I know you did a lot of research about this for maplibre-rs. Do you have any input on how to approach this?

quietlychris commented 7 months ago

I believe that some other projects like Bevy are looking at using cosmic-text for this, per https://github.com/bevyengine/bevy/issues/7616

Maximkaaa commented 7 months ago

Yes, glyphon also uses cosmic-text for text shaping, but it also includes glyph texture atlases.

maxammann commented 7 months ago

You can find some of my notes here: https://maplibre.org/maplibre-rs/book/development-documents/font-rendering.html

Text rendering can have varying challenges based on what you want achieve. MapLibre is able to place text in the "game world". So you can actually walk around text and have text "printed onto the map". If you want to have the same then the problem becomes very hard as you have to come up with a technique to achieve that (SDFs, Bezier rendering etc).

I think that goal is maybe too ambitious and not needed in 99% of the cases. If you want to render text in the screen space then you probably can get away with using something like glyphon.

Note though, if you want perfect text rendering for all languages you kind of still have to use harfbuzz and not rustybuzz :/

Maximkaaa commented 7 months ago

I've spent some time trying things out and researching and here is what I've come with: https://maximkaaa.github.io/galileo/blog/posts/text_rendering_design/

grovesNL commented 7 months ago

In case it's helpful, I'm definitely interested in adding transformations into glyphon. We just need to decide on the right approach to expose it. iced currently uses glyphon for regular text but uses the vector representation for transforms (e.g., https://github.com/iced-rs/iced/pull/2204) that doesn't go through the glyph cache, so it would be really cool to upstream something similar to glyphon itself.

Maximkaaa commented 7 months ago

So far I've decided to go to vector only approach first, as it is more general and covers all uses cases. So I don't use glyphon at the moment. But my experiments I already see less than ideal results in some cases due to lack of hinting. So I will definitely be looking into glyphon later.

That being said, even for vector representation of glyphs, caching tessellated glyphs would be very useful. I believe that swash does cache path commands for glyphs similar to how it caches rasterizations. But the most computationally heavy operation for these is tessellation, so if glyphon would take care of caching that, that would be great.

As for transformations themselves, in my case I would have to line up glyphs along a path, which means I would have to apply different set of rotations and translates to each glyph, not to the whole glyph run. This is something SVG can do, but I'm not sure if this is in scope of glyphon. If not, just convenience functions to rotate/translate vector glyphs would be helpful but not essential.

grovesNL commented 7 months ago

Yeah for sure, I'd like to cache tessellated glyphs if we went to vectors for transformations.

The path use case is interesting. I don't know how SVG renderers handle this but it seems like you'd need deep integration with shaping/layout to do per-character transformations (vs. projecting the entire run along the path somehow).

maxammann commented 7 months ago

Great blog post!

So far I've decided to go to vector only approach first, as it is more general and covers all uses cases.

Do you go with the maplibre signed distance fields or plainly tessellating the curves in the font files?

If it is the latter then you likely will hit performance limitations.

maxammann commented 7 months ago

btw https://github.com/wipfli/about-text-rendering-in-maplibre

Maximkaaa commented 7 months ago

Thanks for the link, @maxammann , it is very well written. And as follows from the article, using SDF has some issues also. So far in my tests I haven't faced any performance issues, but I didn't test for performance in any depth. Anyway, my main concern at the moment is to create a solution that will work correctly for all inputs and on all platforms, and MapLibre approach doesn't allow both of these (shaping problem and using server-side rasterizing are big no-no for me). After that is done, performance can be improved by caching, multithreading etc., but that should be done only after testing to see, which part of the code would be a bottleneck.

kylerchin commented 7 months ago

How long will it take for this to be released?

Maximkaaa commented 7 months ago

I don't have much time to work on Galileo at the moment, but hope to roll out first iteration of labels late next week. Implementing everything that is planned including optimizations for web, advanced rasterization and caching will take a while.

shujaat-github commented 6 months ago

@Maximkaaa The following repository might be helpful for font rendering: https://github.com/mooman219/fontdue