Skip to content

Commit

Permalink
fix font atlas overflow (#495)
Browse files Browse the repository at this point in the history
manage font_atlas overflow
  • Loading branch information
verzuz committed Sep 16, 2020
1 parent 2b0ee24 commit d4ab2f4
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 22 deletions.
5 changes: 3 additions & 2 deletions crates/bevy_text/src/font_atlas.rs
Expand Up @@ -39,15 +39,16 @@ impl FontAtlas {
texture_atlases: &mut Assets<TextureAtlas>,
character: char,
texture: &Texture,
) {
) -> bool {
let texture_atlas = texture_atlases.get_mut(&self.texture_atlas).unwrap();
if let Some(index) =
self.dynamic_texture_atlas_builder
.add_texture(texture_atlas, textures, texture)
{
self.glyph_to_index.insert(character, index);
true
} else {
panic!("ran out of space in font atlas");
false
}
}
}
53 changes: 43 additions & 10 deletions crates/bevy_text/src/font_atlas_set.rs
Expand Up @@ -13,7 +13,7 @@ type FontSizeKey = FloatOrd;
#[derive(Default)]
pub struct FontAtlasSet {
font: Handle<Font>,
font_atlases: HashMap<FontSizeKey, FontAtlas>,
font_atlases: HashMap<FontSizeKey, Vec<FontAtlas>>,
}

#[derive(Debug)]
Expand All @@ -30,15 +30,17 @@ 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()
}

pub fn has_char(&self, character: char, font_size: f32) -> bool {
self.font_atlases
.get(&FloatOrd(font_size))
.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())
})
}

Expand All @@ -52,10 +54,16 @@ impl FontAtlasSet {
) -> f32 {
let font = fonts.get(&self.font).unwrap();
let scaled_font = ab_glyph::Font::as_scaled(&font.font, font_size);
let font_atlas = self
let font_atlases = self
.font_atlases
.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 width = 0.0;
Expand All @@ -67,10 +75,30 @@ impl FontAtlasSet {
if let Some(last_glyph) = last_glyph.take() {
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()) {
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);
Expand All @@ -85,9 +113,14 @@ impl FontAtlasSet {
.get(&FloatOrd(font_size))
.and_then(|font_atlas| {
font_atlas
.get_char_index(character)
.map(|char_index| GlyphAtlasInfo {
texture_atlas: font_atlas.texture_atlas,
.iter()
.find_map(|atlas| {
atlas
.get_char_index(character)
.map(|char_index| (char_index, atlas.texture_atlas))
})
.map(|(char_index, texture_atlas)| GlyphAtlasInfo {
texture_atlas,
char_index,
})
})
Expand Down
24 changes: 14 additions & 10 deletions examples/ui/font_atlas_debug.rs
Expand Up @@ -12,15 +12,15 @@ fn main() {
}

struct State {
added: bool,
atlas_count: u32,
handle: Handle<Font>,
timer: Timer,
}

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

0 comments on commit d4ab2f4

Please sign in to comment.