glifo: Always draw non-hinted glyphs at upem size#1629
Conversation
4d699a7 to
513b096
Compare
|
Hmm, it is a bit unfortunate that basically all tests had to be regenerated. I would really be curious to hear from @dfrg first if he sees any issue with doing this. |
|
No issues at all. In fact, I intended to implement this optimization for unhinted glyphs in vello classic but never got around to it. There are some older fonts where hinting is required for correct rasterization so I would keep that in mind if the aim is production quality. See googlefonts/fontations#1303 edit: I suppose “no issues” was a bit strong. More like “one minor issue…” |
|
Thanks a lot for confirming! Right, I remember that, though the issue is orthogonal to this PR. This could be fixed in a follow-up if we want to. |
| buffer: f32, | ||
| ) -> impl Iterator<Item = Rect> + 'c { | ||
| let font_ref = self.prepared_run.font.as_skrifa(); | ||
| let upem: f32 = font_ref.head().map(|h| h.units_per_em()).unwrap().into(); |
There was a problem hiding this comment.
upem and the cache-size decision are pure functions of the prepared run, but they're computed independently in draw_glyphs and here. Storing the resolved cache_size (and ideally upem) on PreparedGlyphRun during prepare_glyph_run would prevent these two consumers from drifting and remove the redundant font_ref.head() lookup.
There was a problem hiding this comment.
Storing the cache_size on PreparedGlyphRun is not quite possible because at that point we don't know yet whether we are filling or stroking (unless we precompute both, which seems a bit ugly to me too 😄).
Will precompute the upem though, as you suggested.
| self.prepared_run.font.data.id(), | ||
| self.prepared_run.font.index, | ||
| draw_props.font_size, | ||
| scale_props.cache_size, |
There was a problem hiding this comment.
If you pass upem here as well, I think you can communicate the intent a bit more clearly when building draw_settings. Essentially, you could write something like the following:
let size_param = if scale_props.cache_size == upem {
Size::unscaled()
} else {
Size::new(scale_props.cache_size)
};
DrawSettings::unhinted(size_param, var_key.0)There was a problem hiding this comment.
Hmm I've not changed this for now because we already have soo many arguments in that function. Feel free to let me know if you feel strongly about it and then I'll do it in a follow-up.
I don't remember the numbers, but seems like ~20-25%? It's definitely a big improvement though! Also thanks for the comments, will try to address them soon! |
c502a94 to
dfb833f
Compare
Problem
Right now, we always draw glyph outlines using skrifa at the given font size we are currently rendering. This causes two problems:
Solution
The insight is that in the common case of having an unhinted and not-stroked glyph, it should be totally fine to just always outline the glyph at upem size and then scale it to the appropriate font size with a simple transform. I believe internally skrifa does something slightly different to keep compatibility with freetype, but in general this shouldn't cause any problems apart from small pixel differences (@dfrg can you confirm?).
Performance
When continuously zooming on our vello text scene, I get the following:
Before:

After:

As you can see, the skrifa outlining part completely disappears because each glyph is only outlined once and everything can therefore stay in the cache. The glyph cache maintenance part also goes away since there is no cache thrashing anymore.