fix font atlas overflow (#495)

manage font_atlas overflow
This commit is contained in:
verzuz 2020-09-16 03:06:10 +02:00 committed by GitHub
parent 2b0ee24a5d
commit d4ab2f4d47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 22 deletions

View File

@ -39,15 +39,16 @@ impl FontAtlas {
texture_atlases: &mut Assets<TextureAtlas>, texture_atlases: &mut Assets<TextureAtlas>,
character: char, character: char,
texture: &Texture, texture: &Texture,
) { ) -> bool {
let texture_atlas = texture_atlases.get_mut(&self.texture_atlas).unwrap(); let texture_atlas = texture_atlases.get_mut(&self.texture_atlas).unwrap();
if let Some(index) = if let Some(index) =
self.dynamic_texture_atlas_builder self.dynamic_texture_atlas_builder
.add_texture(texture_atlas, textures, texture) .add_texture(texture_atlas, textures, texture)
{ {
self.glyph_to_index.insert(character, index); self.glyph_to_index.insert(character, index);
true
} else { } else {
panic!("ran out of space in font atlas"); false
} }
} }
} }

View File

@ -13,7 +13,7 @@ type FontSizeKey = FloatOrd;
#[derive(Default)] #[derive(Default)]
pub struct FontAtlasSet { pub struct FontAtlasSet {
font: Handle<Font>, font: Handle<Font>,
font_atlases: HashMap<FontSizeKey, FontAtlas>, font_atlases: HashMap<FontSizeKey, Vec<FontAtlas>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -30,7 +30,7 @@ impl FontAtlasSet {
} }
} }
pub fn iter(&self) -> impl Iterator<Item = (&FontSizeKey, &FontAtlas)> { pub fn iter(&self) -> impl Iterator<Item = (&FontSizeKey, &Vec<FontAtlas>)> {
self.font_atlases.iter() self.font_atlases.iter()
} }
@ -38,7 +38,9 @@ impl FontAtlasSet {
self.font_atlases self.font_atlases
.get(&FloatOrd(font_size)) .get(&FloatOrd(font_size))
.map_or(false, |font_atlas| { .map_or(false, |font_atlas| {
font_atlas.get_char_index(character).is_some() font_atlas
.iter()
.any(|atlas| atlas.get_char_index(character).is_some())
}) })
} }
@ -52,10 +54,16 @@ impl FontAtlasSet {
) -> f32 { ) -> f32 {
let font = fonts.get(&self.font).unwrap(); let font = fonts.get(&self.font).unwrap();
let scaled_font = ab_glyph::Font::as_scaled(&font.font, font_size); let scaled_font = ab_glyph::Font::as_scaled(&font.font, font_size);
let font_atlas = self let font_atlases = self
.font_atlases .font_atlases
.entry(FloatOrd(font_size)) .entry(FloatOrd(font_size))
.or_insert_with(|| FontAtlas::new(textures, texture_atlases, Vec2::new(512.0, 512.0))); .or_insert_with(|| {
vec![FontAtlas::new(
textures,
texture_atlases,
Vec2::new(512.0, 512.0),
)]
});
let mut last_glyph: Option<Glyph> = None; let mut last_glyph: Option<Glyph> = None;
let mut width = 0.0; let mut width = 0.0;
@ -67,10 +75,30 @@ impl FontAtlasSet {
if let Some(last_glyph) = last_glyph.take() { if let Some(last_glyph) = last_glyph.take() {
width += scaled_font.kern(last_glyph.id, glyph.id); width += scaled_font.kern(last_glyph.id, glyph.id);
} }
if font_atlas.get_char_index(character).is_none() { if !font_atlases
.iter()
.any(|atlas| atlas.get_char_index(character).is_some())
{
if let Some(outlined_glyph) = scaled_font.outline_glyph(glyph.clone()) { if let Some(outlined_glyph) = scaled_font.outline_glyph(glyph.clone()) {
let glyph_texture = Font::get_outlined_glyph_texture(outlined_glyph); let glyph_texture = Font::get_outlined_glyph_texture(outlined_glyph);
font_atlas.add_char(textures, texture_atlases, character, &glyph_texture); let add_char_to_font_atlas = |atlas: &mut FontAtlas| -> bool {
atlas.add_char(textures, texture_atlases, character, &glyph_texture)
};
if !font_atlases.iter_mut().any(add_char_to_font_atlas) {
font_atlases.push(FontAtlas::new(
textures,
texture_atlases,
Vec2::new(512.0, 512.0),
));
if !font_atlases.last_mut().unwrap().add_char(
textures,
texture_atlases,
character,
&glyph_texture,
) {
panic!("could not add character to newly created FontAtlas");
}
}
} }
} }
width += scaled_font.h_advance(glyph.id); width += scaled_font.h_advance(glyph.id);
@ -85,9 +113,14 @@ impl FontAtlasSet {
.get(&FloatOrd(font_size)) .get(&FloatOrd(font_size))
.and_then(|font_atlas| { .and_then(|font_atlas| {
font_atlas font_atlas
.get_char_index(character) .iter()
.map(|char_index| GlyphAtlasInfo { .find_map(|atlas| {
texture_atlas: font_atlas.texture_atlas, atlas
.get_char_index(character)
.map(|char_index| (char_index, atlas.texture_atlas))
})
.map(|(char_index, texture_atlas)| GlyphAtlasInfo {
texture_atlas,
char_index, char_index,
}) })
}) })

View File

@ -12,7 +12,7 @@ fn main() {
} }
struct State { struct State {
added: bool, atlas_count: u32,
handle: Handle<Font>, handle: Handle<Font>,
timer: Timer, timer: Timer,
} }
@ -20,7 +20,7 @@ struct State {
impl Default for State { impl Default for State {
fn default() -> Self { fn default() -> Self {
Self { Self {
added: false, atlas_count: 0,
handle: Handle::default(), handle: Handle::default(),
timer: Timer::from_seconds(0.05, true), timer: Timer::from_seconds(0.05, true),
} }
@ -34,20 +34,23 @@ fn atlas_render_system(
font_atlas_sets: Res<Assets<FontAtlasSet>>, font_atlas_sets: Res<Assets<FontAtlasSet>>,
texture_atlases: Res<Assets<TextureAtlas>>, texture_atlases: Res<Assets<TextureAtlas>>,
) { ) {
if state.added {
return;
}
if let Some(set) = font_atlas_sets.get(&state.handle.as_handle::<FontAtlasSet>()) { if let Some(set) = font_atlas_sets.get(&state.handle.as_handle::<FontAtlasSet>()) {
if let Some((_size, font_atlas)) = set.iter().next() { if let Some((_size, font_atlas)) = set.iter().next() {
state.added = true; let x_offset = state.atlas_count as f32;
let texture_atlas = texture_atlases.get(&font_atlas.texture_atlas).unwrap(); if state.atlas_count == font_atlas.len() as u32 {
return;
}
let texture_atlas = texture_atlases
.get(&font_atlas[state.atlas_count as usize].texture_atlas)
.unwrap();
state.atlas_count += 1;
commands.spawn(ImageComponents { commands.spawn(ImageComponents {
material: materials.add(texture_atlas.texture.into()), material: materials.add(texture_atlas.texture.into()),
style: Style { style: Style {
position_type: PositionType::Absolute, position_type: PositionType::Absolute,
position: Rect { position: Rect {
top: Val::Px(0.0), top: Val::Px(0.0),
left: Val::Px(0.0), left: Val::Px(512.0 * x_offset),
..Default::default() ..Default::default()
}, },
..Default::default() ..Default::default()
@ -61,8 +64,9 @@ fn atlas_render_system(
fn text_update_system(mut state: ResMut<State>, time: Res<Time>, mut query: Query<&mut Text>) { fn text_update_system(mut state: ResMut<State>, time: Res<Time>, mut query: Query<&mut Text>) {
for mut text in &mut query.iter() { for mut text in &mut query.iter() {
state.timer.tick(time.delta_seconds); state.timer.tick(time.delta_seconds);
if state.timer.finished { let c = rand::random::<u8>() as char;
text.value = format!("{}", rand::random::<u8>() as char); if !text.value.contains(c) && state.timer.finished {
text.value = format!("{}{}", text.value, c);
state.timer.reset(); state.timer.reset();
} }
} }