-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blurry Text Rendering When Using a Pixel-style Font #1790
Comments
I just inspected a render with RenderDoc and discovered that the font is actually blurred in the font texture itself, so that rules out linear filtering: I guess that means we need to tweak the rasterization step to somehow line up the texture pixels with the bitmap-style font pixels. Here's the issue, this is a Unless font hinting is th right solution, like mentioned here: #56 (comment). Another possible solution could be to support bitmap font formats directly, such as BDF, which seems to be somewhat standard for pixel-style fonts. I made a BDF parser with only 211 lines of code ( and the peg crate ). We would just have to integrate that with the egui font rasterization. I'm just not sure what that looks like in Egui right now, so I'd have to investigate whether or not that would be feasible. |
I was hopeful that the linked PR would fix this, but it does not. I put the CozetteVector.ttf into an egui hello world with and without the patch applied. The difference is only in positioning and relative sizing. Both have the same aliasing artifacts around the edge of the text, no matter what font size is used. But I suppose it isn't too surprising, because the vector fonts are specifically designed to scale arbitrarily and antialiasing is actually a good thing for this use case. To put it another way, the Cozette README describes the situation:
(Also consider sub-pixel rendering with ClearType, FreeType, etc. a la #2356) Support for bitmap fonts seems like the right way forward. Although rasterization of vector fonts can be made a little better for "pixel font" styles by disabling texture filtering/antialiasing, it's more trouble than it is worth. |
I would be very interested in support for bitmap fonts; I'm working on a pixel game that is low resolution by design and I've been struggling to get text to look good. |
I hacked in support for bitmap fonts and it makes a world of difference at low resolutions; much crisper text. Essentially what I did was remove I would suggest that, rather than adding native support for BDF or other bitmap font formats into the library directly, it could make more sense to put a layer of abstraction between The |
This is probably good for a hack, but that exposes a lot of internal details to a public API. A very leaky abstraction, and it would make changing implementation details (related to these internals) very difficult to update without breaking compatibility. The reasoning behind suggesting "something like BDF" is that this is a stable format. Everything behind the abstraction boundary can be replaced and callers would never notice or care. The ideal for abstractions. Anyway, |
It doesn't strike me as particularly leaky, having just hacked it in. All it really needed was a draw routine that returned a list of x,y,v points, which seems super generic and something any font library would likely have to provide, and the ascent/descent/kerning/bounding box info, which, again, is something just about any font API would have to provide. I mentioned that it'd be used to create the atlas texture, but the API itself wouldn't have to know anything about that, or even that there is one, for example. It's somewhat common for bitmap fonts to be bespoke, so generating a BDF would be a bit of PITA compared to making an adapter that returns much the same info if you're just making it in Aseprite or something. But I won't press the issue. I'm sure there are plenty of uses for a more expansive API than what I'm describing, given all the features of even something as ancient as BDF has that I have ignored. |
I'm talking about abstractions leaking implementation details. :) There's no reason that someone who wants to put text into the UI should have to know anything about texture atlases. Let alone the specific texture format or the need for Like I said, probably good enough for just working around this issue, but I would be concerned about any proposal to add it to the public API. As far as which bitmap font formats to support, I'm indifferent on the matter. It could be "PNG + fixed glyph size" and that would be enough for just about any use case for fixed-width pixel fonts. More opinionated formats have the advantage that they are not limited to fixed-width and provide typographical information that is helpful for the rasterizer (kerning and hinting, etc). |
Right; like I said, I don't think it would need to know that there is a texture atlas, let alone texture formats, though it would need some way of indicating where pixels are of course. This is a rough sketch of what I mean: /// `FontProvider` is a trait that can be implemented by the user to provide fonts to `epaint`.
pub trait FontProvider {
type FontImpl: Font;
/// Returns the font for the given point `size` and `style`.
fn font(&self, size: f32, style: TextStyle) -> Self::FontImpl;
}
/// `Font` is a trait that can be implemented by the user to represent a specific, sized font to `epaint`.
pub trait Font {
type GlyphImpl: Glyph;
type Iter: Iterator<Item = char>;
/// Returns the ascent of the font, in points.
fn ascent(&self) -> f32;
/// Returns the descent of the font, in points.
fn descent(&self) -> f32;
/// Returns the glyph for the given character.
fn get(&self, cp: char) -> &Self::GlyphImpl;
/// Returns an iterator over all supported characters.
fn characters(&self) -> Self::Iter;
}
pub trait Glyph {
/// Returns the bounding box for this glyph: width, height, x offset, y offset.
fn bbx(&self) -> (i32, i32, i32, i32);
/// Returns the amount to advance to the next character.
fn advancement_width(&self) -> i32;
/// Draw the glyph using cb(x, y, alpha).
/// x,y must be within bbx().
/// alpha is 0.0 to 1.0.
fn draw(&self, cb: &mut dyn FnMut(u32, u32, f32));
} The above isn't real, and definitely isn't correct, but the idea would be that any font implementation should be able to easily implement the above primitives, be that I do think it does leak quite a bit implicitly in the sense that it'd be assumed that (as for ignoring |
Sounds good in theory! But that I can imagine a slightly different API that instead passes a
Anyway, yes, it's within the realm of possibility. I don't know if it's actually better than just "PNG + size"... At least in that case, a font provider doesn't have to do anything at runtime. It's a much easier API to use; "set it and forget it."
The missing piece here is that fonts designed for "low DPI" displays ( And vice versa. Fonts designed for The right way to handle this is with multiple size variations for your font. Say one for |
Ah, yeah, I assumed once the texture atlas was made, those 'draw' calls would never occur again and thus amortized, but it doesn't matter because your solution is strictly better anyway.
Sure, in the normal case; in mine, I'm scaling the entire screen in post-processing afterward along with everything else and there's nothing dynamic about anything (think old school CRPGs). In any case, you're right for the general case, though I'm guessing very few who reach for a bitmap font are going to support more than one size. As an aside, I happened to take a before and after, as a point of comparison: The difference is more noticeable because of that post-processing I'm doing to fake CRT effects; a pretty niche case for sure. I was definitely happy when I saw the difference, though. |
Describe the bug
When using a pixel-style font like Cozette or Ark I notice that the edges of the font get kind of blurred slightly most of the time, though if you position and size the window/widget to just the right spot the edges will be clear.
Here's a screenshot:
The effect is subtle, but zooming in makes it more obvious:
But the button text happens to line up fine and end up clear:
To Reproduce
It seems to be reproducible by simply uploading a font such as font like Cozette or Ark and placing a label.
Expected behavior
The edges of the font should stay crisply lined up with the screen's pixels
Desktop (please complete the following information):
Additional context
Related to fishfolk/punchy#38.
I wonder if this might be due to linear filtering on the font texture. 🤔 It might be simple enough to allow you to specify that a certain font should have it's texture generated with nearest filtering mode. We would have to test to see if switching the filtering mode will fix the issue.
The text was updated successfully, but these errors were encountered: