glifo: Simplify COLR glyph handling#1605
Conversation
|
Does this apply to regular skewed glyphs? I imagine skewed COLR glyphs are pretty uncommon. But skewed regular glyphs for "fake italic" would be significantly more common. |
|
Nope, it only applies for cached glyphs. And caching i never applied if a glyph is skewed. |
|
Sorry, that was an accident 😅 |
There was a problem hiding this comment.
I think this approach has two problems:
- Intermediate pixmap is sized in font units, not device pixels:
bbox comes from bounding_box(..., Size::unscaled()), and create_colr_glyph uses it directly:
let (pix_width, pix_height) = (
metrics.bbox.width().ceil() as u16,
metrics.bbox.height().ceil() as u16,
);
let draw_transform = Affine::translate((-metrics.bbox.x0, -metrics.bbox.y0));draw_transform has no scale, so 1 font unit → 1 texel. For Noto Color Emoji that's huge texture regardless of font size. On main the same glyph should use much less space.
- Atlas caching is silently disabled in
insert_and_render_colr:
supports_atlas_caching requires [a, d] ≈ [1, -1]. On main, the outer transform cancelled the scale baked into metrics.transform. The new outer transform drops that cancellation, so any non-unit scale flows straight through.
I added a println! to the UnsupportedTransform branch and it fires for every glyph in random_text scene example (I only added colr glyphs there). Cache rejected unconditionally, full colr paint repeats every frame.
There was a problem hiding this comment.
Uff, thanks for catching that. 😔 I guess that explains why the cached tests all changed... Would be nice if there were some way of more easily telling whether caching was used or not.
There was a problem hiding this comment.
Yeah, I agree, the caching not working doesn’t seem consistent with the intention of enabling the cache. I don’t have a solid answer here, but it’d be nice to provide at least some hint about it. Ideally, we could track cases where caching somehow breaks even when it’s enabled. At the very least, maybe we could add a warning in dev mode?
As was evidenced in linebender#1605, it is pretty easy to make changes to the caching logic and then accidentally break the whole caching mechanism. To hopefully provide some safe-guarding against this, this PR adds 6 unit tests, each ensuring that the different configurations for outline/bitmap/COLR glyphs result in the expected usage of the cache. In case atlas caching is disabled, the cache should stay untouched. In case it's enabled, the glyphs should actually end up landing in the cache.
Initially, in Vello CPU, any COLR glyph would first be rendered into an intermediate pixmap and that pixmap would then be rendered into the surface. Because of this, in case there was a skewing factor in the glyph, instead of just rasterizing the glyph at its normal font size, we would apply an additional scaling factor such that it would be rasterized at a higher solution, avoiding aliasing artifacts that arise from the skewing.
However, since the most recent changes, things are different: By default, glyphs are always directly rendered into a surface. Glyphs are only cached if they don't have any skewing factor (if in the future we decide that we also want glyph caching for rotated glyphs, we can easily just fold this into the font size again). Therefore, this additional scaling procedure becomes unnecessary. By doing so, the code becomes a easier to understand (in my opinion).
As a nice side effect, this PR also fixes a discrepancy between cached and non-cached rendering that existed previously:
rec.mp4
As you can see, previously (on the left) cached and non-cached glyphs would have slightly different positions. Now, they seem to have the same position. I must confess I don't fully understand why this was the case, but the tests don't lie! 😄