microsoft / terminal

The new Windows Terminal and the original Windows console host, all in the same place!
MIT License
95.04k stars 8.23k forks source link

Italics & Bold not rendering properly if they are the final character - text looks "cut off" #9381

Closed Nosamdaman closed 1 year ago

Nosamdaman commented 3 years ago

Environment

Windows build number: [Version 10.0.19042.844]
Windows Terminal version: 1.6.10571.0

Any other software?
Ubuntu using WSL 1
Fish Shell

Steps to reproduce

Print any italic text to the terminal

Expected behavior

All characters should be italicized and fully visible.

Actual behavior

The last character before a newline is cut-off where the italic character would bleed into the next character. Note this occurs when using both Cascadia Code and FiraCode in my testing.

After further testing, we've determined that the issue is related to the cleartype antialiasing setting. Using grayscale eliminates the issue.

image

lhecker commented 2 years ago

In 1.16 those glyphs won't be cut off anymore. But to retain a proper monospace grid we should implement your suggestion @DHowett.

j4james commented 2 years ago

@lhecker If we're going to force the cells to be wide enough to completely fit italic characters, I suspect that's going to make the letter spacing unnecessarily large for quite a few fonts. That will be particularly annoying for users that aren't even using italics.

I thought at one point that you had planned to allow characters to overflow their cell, which I assumed would largely solve this problem (at leat if we're also rendering the background in a separate pass - I'm not sure if that's true yet).

I can't say for definite that's what everyone else does, but I know at least some terminals do something like this. For example this is a screenshot from Mintty rendering italics with the background changing between characters.

image

scallaway commented 2 years ago

@lhecker I thought at one point that you had planned to allow characters to overflow their cell, which I assumed would largely solve this problem (at leat if we're also rendering the background in a separate pass - I'm not sure if that's true yet).

This would be the way I'd expect this to be implemented?

I'm not sure, however, whether this uncovers other issues surrounding obscure placement of characters, especially with other glyphs.

I haven't had a chance to test the latest myself yet, but will try to give it a go for my use case in the next day.

lhecker commented 2 years ago

I thought at one point that you had planned to allow characters to overflow their cell, which I assumed would largely solve this problem (at leat if we're also rendering the background in a separate pass - I'm not sure if that's true yet).

Yeah I still do, but it's not trivial to decide on a proper heuristic here. What I was planning to solve in the near term is joining ambiguous emojis with a trailing whitespace to make Windows Terminal behave like other terminals in that regard. Implementing the heuristic for that is pretty simple after all.

Implementing a glyph atlas with arbitrarily sized glyphs correctly and properly is a lot more annoying. This will require the use of a bin packing algorithm to store glyph textures. Then during rendering you have to render each glyph on its own rectangular polygon (quad). This is how most text renderers including Direct2D do it. It's easy to do this sloppy and hard to do correctly.

After all, you can't just draw each glyph on its own quad. What if the overlapping glyphs intentionally form a ligature? Overlapping them by simply alpha-blending them would mean that the anti-aliased border of the glyph ends up too opaque at the point of the overlap. It also doesn't work with ClearType which a lot of our users use (ClearType doesn't support alpha blending). Joining overlapping glyphs and rasterizing them as one large glyph solves the problem.

But what is the heuristic here? If you simply join all overlapping glyphs you might get a perfect result, but now a screen full of slanted/italic text would all be rendered as one huge glyph with effectively no caching at all. The performance would end up being terrible. Given that Direct2D already handles this perfectly, it might be easier to port our text rendering improvements to Direct2D than it is to replicate Direct2Ds text renderer in Windows Terminal in full.

I'm not a text rendering expert and I've always considered our custom text renderer ("AtlasEngine") to be a "hack" which just works around our immediate performance problems we had. It's a nice bonus that it performs much much faster than Direct2D ever could, thanks to us assuming that each glyph fits into the terminal's raster grid, but clearly this isn't a perfect solution either.

If anyone is knowledgeable about text rendering and has ideas how to improve ours I'd be happy to hear about it.

I haven't had a chance to test the latest myself yet, but will try to give it a go for my use case in the next day.

1.16 hasn't been released yet, but will be most likely this week.

j4james commented 2 years ago

But what is the heuristic here? If you simply join all overlapping glyphs you might get a perfect result, but now a screen full of slanted/italic text would all be rendered as one huge glyph

But you surely don't need to join overlapping glyphs just because they're slanted? I would have thought the only time you really care about joining is if you've got an actual ligature, which I assumed you'd know from the font metrics somehow. Or am I wrong about that?

I accept you might still have a situation where you've got something like an italic W overlapping a non-italic H, with artifacts where the characters intersect, but it seems like that should be fairly rare, and even when it happens I suspect it would be less jarring than having excessive letter spacing everywhere.

I'm not super concerned about any of this stuff though. Just wanted to offer my thoughts on the subject. But I know very little about text rendering, so if I'm not making any sense, feel free to disregard.

lhecker commented 2 years ago

But you surely don't need to join overlapping glyphs just because they're slanted? I would have thought the only time you really care about joining is if you've got an actual ligature, which I assumed you'd know from the font metrics somehow. Or am I wrong about that?

For text rendering with grayscale antialiasing this would mostly work, apart from an uneven appearance when glyphs do intersect. This is the approach I would choose if I had to make this change in the near-ish future, because it provides 80% of the value (overlapping glyphs mostly appear to work) for 20% of the cost (only a minor performance impact). If ClearType is enabled I'd probably force glyphs into the cell grid the way it works now, due to the lack of an alpha channel for ClearType glyphs.

But in either case this does require a non-trivial amount of work (due to the bin packing with conservative memory management & quad layout for each glyph) and and I still need to work on a lot of other rendering improvements which IMO add just as much value to Windows Terminal (looking at you, sixel support in AtlasEngine, our 4th biggest issue). Until then I'm hoping that the new cell-fitting algorithm (#13549) which ships in 1.16 next week improves the situation around italic/bold glyphs so much so that it's far less of an issue for most users, most of the time.

Edit: The cell-fitting thing didn't work out. 🥲

zadjii-msft commented 1 year ago

Hey this might have been fixed by #14959. I haven't been following this thread as closely, but I'm pretty sure this thread ended up being one of the more central premises of that approach.

Talking with @lhecker, there's still the chance that text will be cut off on the right side of the window, by the margins, but that seems like a much smaller subset of this thread.

Nosamdaman commented 1 year ago

I agree, text getting cutoff at the edge is pretty outside of the scope of my original complaint, thanks for getting this one sorted out guys!