Open overdrivenpotato opened 2 years ago
When hand writing the vectorization, I did some AB testing with built in gamma correction which went largely unnoticeable. People that did notice it, preferred it without.
However, when working with linear u8 values, a nonlinear function like sRGB maps u8 -> u8 values with some precision loss.
Gamma correction processes work on images already, which typically have 8 bit channels, so I'm not convinced the 0.3% max precision loss is perceptible.
If you can provide convincing material (Like a PR) showing the legibility of output is noticeably improved by the small precision improvement without a significant cost to the performance, then we can use that to substantiate this issue.
If you're rendering fontdue with some kind of linear blending (e.g. on a GPU), it looks fine. But if you're doing blending in a non-linear space it tends to look pretty bad in some cases:
The top text in the above picture has a gamma adjustment, while the bottom does not (you might have to click through the image to view it in full quality). The blending is occurring in sRGB space. It's up to you whether you think the bottom text looks bad; to me it looks broken.
So naturally you can use a u8 -> u8
mapping, but this is worse than 0.3% error. For example, a value 2/255
gets mapped to 22/255
from sRGB to linear, while 3/255
becomes 28/255
. So you lose the values 23, 24, 25, 26, 27
, resulting in visible artifacts. And a performance problem now is that you now have to incur an entire other loop over the generated data to do that mapping, requiring more memory loads and stores.
What about adding a method like Font::rasterize_gamma<G: Gamma>(...)
, with a Gamma
trait like:
trait Gamma {
fn to_u8(linear: f32) -> u8;
}
(SIMD arguments left out for sake of example). This could be entirely inlined, avoiding both precision errors and the extra cost of a round trip through memory for every pixel. It would be trivial to implement simple truncation or rounding to keep the current behaviour.
PR #115 fixes issue #114 by converting the output
u8
values to sRGB after fontdue creates a coverage vector. However, when working with linearu8
values, a nonlinear function like sRGB mapsu8 -> u8
values with some precision loss. When rendering text, it is also sometimes preferable to use an empirically determined gamma value like1.45
, which has the same precision issue.This can probably be fixed by exposing a rasterization method that outputs
Vec<f32>
, or alternatively via a more clever API with the use of aGamma
trait in order to avoid the memory overhead off32
values.