Apply gamma correction to compute the SDF#61
Conversation
|
Interesting! BTW some articles say that fonts should be rendered at 1.4 gamma:
I don't know what gamma the browsers use. If they use 1.4, then using 1.0 / 1.4 instead of 1.0 / 2.2 will probably make your glyphs look closer to the reference. |
In this case, we're reading the antialiased glyph directly from a Canvas2D rendering context. The pixel values we get are already gamma-encoded (typically with a gamma of 2.2), as per the sRGB standard for images. This gamma encoding happens at the image level, independent of the browser’s display pipeline or the monitor’s characteristics. While browsers may apply further color management or gamma correction when displaying the final image on screen, that process doesn't affect the pixel values we read from the canvas. So, for the purpose of decoding the coverage values from the rendered glyph, we should apply the inverse of the standard sRGB gamma (usually 2.2), not a display-specific value like 1.4. |
|
Thanks for the PR, seems like a nice subtle improvement! Although the GIF attached as a static image, so the difference is a bit hard to see. Can you also measure how much this impacts performance of a tiny-sdf run? I assume it's negligible (distance transform takes a lot more time than the initial single pass of reading pixel values), but worth double-checking. |
|
Hi @mourner Without: So about 3% |
|
@xavierjs we could also precompute since alpha values are discrete (only 256 different values), but probably not worth the effort, 3% degradation is fine. It's hard to measure how much the rendering improves, I guess it's somewhat subjective (and we'll always have some minor discrepancy with the way a browser engine renders text) — is it noticeable for your case? Overall I think we can land this. |
|
I observe the difference on my end (Macbook pro M4 Max 16 inch). With the gamma correction, the Arlington label is undistinguishable from the HTML copy, whereas without alpha correction the Arlington label is a bit thinner than the HTML one. |
|
Thanks again! I ended up adding a lookup table optimization that makes it a bit faster than it was before the PR. f2a1550 |
Hi,
Thank you for considering my PR.
Context
When we compute the SDF, we render the glyph with the Canvas2D API, then we compute the distance to the border:
tiny-sdf/index.js
Line 93 in 51c177e
Issue
The glyph is rendered with the Canvas2D API, which applies antialiasing and gamma correction to the output.
Let's take a concrete example:
Consider a pixel that lies exactly on the glyph's border. If we could render this pixel with infinite subpixel precision, half of the pixel would be white (outside the glyph), and half would be black (inside the glyph). In this ideal case, the coverage value
afor the pixel would be0.5(i.e., 50% grey, orrgb(127, 127, 127)), sod = 0.However, due to gamma correction, the actual color displayed for this pixel will not be 50% grey. Instead, we must apply gamma correction to obtain the color that corresponds to 50% luminance. The displayed grey will be
0.5^2.2 ≈ 0.22, which is a much lighter grey:rgb(199, 199, 199), since199 = 255 * (1 - 0.5^2.2).Improvement
So if we want to compute the
dvalue for this pixel (mapping to the black/white coverage), we need to apply the inverse gamma transform:Benchmarks
In these 2 images, the Arlington label on the bottom is displayed in HTML above the canvas. This is the reference.

The label above is rendered using this package.
Without the gamma correction, the glyphs look a bit thinner than the reference:
With the correction, it looks a bit thicker:

Here is a gif for a better comparison:

Ref
https://www.cambridgeincolour.com/tutorials/gamma-correction.htm