renderer/metal: blend rendered text in linear RGB space (aka. Gamma Correction) #4686
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Touches #2125
Before, we were blending rendered text onto the background in sRGB
space, which is linear in perceived color/brightness but not linear in
physical terms.
Correct alpha blending requires decoding from sRGB space into linear RGB
space, performing the blending, and then encoding back to sRGB space for
presenting. See here for a very good article on the whole topic of gamma
and how it relates to sRGB and text rendering:
https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/#antialiasing
In this demonstation, I do the decoding "by hand" in the shader, and
rely on Metal do re-encode as sRGB, by setting the right pixelFormat on
the colorAttachment.
One thing to note is that correct alpha blending in linear space makes
the rendered fonts look even thinner than they already are. When Kitty
introduced alpha blending in linear space, they introduced a
configuration that allows adjusting the gamma/contrast, which will
thicken up the fonts again. Fonts being thinner when doing correct
blending is also something that the article linked above explains, and
you can confirm this behavior for youself by running through an example
alpha blending calculation by hand, on pen and paper, say.
Just letting you know, I'm not at all wedded to this PR. 🕊️ I got interested in the topic because font rendering on Ghostty looked somewhat "flimsy" to me, so I dug down into the rabbit hole of sRGB, gamma correction, etc. If you have other plans or someone is already working on this I can close the PR. If you're keen to help me shepherd this in, though, I'd be very happy to respond to any comments/suggestions!
TODO/Notes before merging:
conversions/decode. Which benchmarks should I be running?
might have the same issues. I could work on that one next.
Screenshots
You might have to zoom in/pixel peep on these.
This shows how fonts look even thinner now, as the above article also describes:
We can see there's no more dark color artifacts around the edged:
And here I demonstrate how we could thicken the fonts again by adding adjustable "gain" (name totally TBD) to the rendered font's alpha:
Here's a yet more thorough comparison. We have browsers up top (Firefox, Safari, Chrome), of which I'd say Firefox does not blend in linear space but Safari and Chrome do. And I'd say Safari looks best to my eyes. In the bottom row we have Ghostty main and Terminal, which blend in sRGB space (without gamma correction), and then Kitty and this branch on the bottom right which do blend in linear space (with gamma correction). Here it also really helps to click through to the screenshot and zoom in. But the changes are most obvious when going in there with Digital Colour Meter.