mirror of
https://github.com/bevyengine/bevy
synced 2025-01-23 10:25:13 +00:00
0b0ef583b6
# Objective The quality of Bevy's text rendering can vary wildly depending on the font, font size, pixel alignment and scale factor. But this situation can be improved dramatically with some small adjustments. ## Solution * Text node positions are rounded to the nearest physical pixel before rendering. * Each glyph texture has a 1-pixel wide transparent border added along its edges. This means font atlases will use more memory because of the extra pixel of padding for each glyph but it's more than worth it I think (although glyph size is increased by 2 pixels on both axes, the net increase is 1 pixel as the font texture atlas's padding has been removed). ## Results Screenshots are from the 'ui' example with a scale factor of 1.5. Things can get much uglier with the right font and worst scale factor<sup>tm</sup>. ### before <img width="300" alt="list-bad-text" src="https://github.com/bevyengine/bevy/assets/27962798/482b384d-8743-4bae-9a65-468ff1b4c301"> ### after <img width="300" alt="good_list_text" src="https://github.com/bevyengine/bevy/assets/27962798/34323b0a-f714-47ba-9728-a59804987bc8"> --- ## Changelog * Font texture atlases are no longer padded. * Each glyph texture has a 1-pixel wide padding added along its edges. * Text node positions are rounded to the nearest physical pixel before rendering.
49 lines
1.7 KiB
Rust
49 lines
1.7 KiB
Rust
use ab_glyph::{FontArc, FontVec, InvalidFont, OutlinedGlyph};
|
|
use bevy_asset::Asset;
|
|
use bevy_reflect::TypePath;
|
|
use bevy_render::{
|
|
render_resource::{Extent3d, TextureDimension, TextureFormat},
|
|
texture::Image,
|
|
};
|
|
|
|
#[derive(Asset, TypePath, Debug, Clone)]
|
|
pub struct Font {
|
|
pub font: FontArc,
|
|
}
|
|
|
|
impl Font {
|
|
pub fn try_from_bytes(font_data: Vec<u8>) -> Result<Self, InvalidFont> {
|
|
let font = FontVec::try_from_vec(font_data)?;
|
|
let font = FontArc::new(font);
|
|
Ok(Font { font })
|
|
}
|
|
|
|
pub fn get_outlined_glyph_texture(outlined_glyph: OutlinedGlyph) -> Image {
|
|
let bounds = outlined_glyph.px_bounds();
|
|
// Increase the length of the glyph texture by 2-pixels on each axis to make space
|
|
// for a pixel wide transparent border along its edges.
|
|
let width = bounds.width() as usize + 2;
|
|
let height = bounds.height() as usize + 2;
|
|
let mut alpha = vec![0.0; width * height];
|
|
outlined_glyph.draw(|x, y, v| {
|
|
// Displace the glyph by 1 pixel on each axis so that it is drawn in the center of the texture.
|
|
// This leaves a pixel wide transparent border around the glyph.
|
|
alpha[(y + 1) as usize * width + x as usize + 1] = v;
|
|
});
|
|
|
|
// TODO: make this texture grayscale
|
|
Image::new(
|
|
Extent3d {
|
|
width: width as u32,
|
|
height: height as u32,
|
|
depth_or_array_layers: 1,
|
|
},
|
|
TextureDimension::D2,
|
|
alpha
|
|
.iter()
|
|
.flat_map(|a| vec![255, 255, 255, (*a * 255.0) as u8])
|
|
.collect::<Vec<u8>>(),
|
|
TextureFormat::Rgba8UnormSrgb,
|
|
)
|
|
}
|
|
}
|