forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
font_atlas_set.rs
154 lines (145 loc) · 5.21 KB
/
font_atlas_set.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use crate::{error::TextError, Font, FontAtlas, TextSettings};
use ab_glyph::{GlyphId, OutlinedGlyph, Point};
use bevy_asset::{Assets, Handle};
use bevy_math::Vec2;
use bevy_reflect::TypeUuid;
use bevy_render::texture::Image;
use bevy_sprite::TextureAtlas;
use bevy_utils::FloatOrd;
use bevy_utils::HashMap;
type FontSizeKey = FloatOrd;
#[derive(TypeUuid)]
#[uuid = "73ba778b-b6b5-4f45-982d-d21b6b86ace2"]
pub struct FontAtlasSet {
font_atlases: HashMap<FontSizeKey, Vec<FontAtlas>>,
queue: Vec<FontSizeKey>,
}
#[derive(Debug, Clone)]
pub struct GlyphAtlasInfo {
pub texture_atlas: Handle<TextureAtlas>,
pub glyph_index: usize,
}
impl Default for FontAtlasSet {
fn default() -> Self {
FontAtlasSet {
font_atlases: HashMap::with_capacity_and_hasher(1, Default::default()),
queue: Vec::new(),
}
}
}
impl FontAtlasSet {
pub fn iter(&self) -> impl Iterator<Item = (&FontSizeKey, &Vec<FontAtlas>)> {
self.font_atlases.iter()
}
pub fn has_glyph(&self, glyph_id: GlyphId, glyph_position: Point, font_size: f32) -> bool {
self.font_atlases
.get(&FloatOrd(font_size))
.map_or(false, |font_atlas| {
font_atlas
.iter()
.any(|atlas| atlas.has_glyph(glyph_id, glyph_position.into()))
})
}
pub fn add_glyph_to_atlas(
&mut self,
texture_atlases: &mut Assets<TextureAtlas>,
textures: &mut Assets<Image>,
outlined_glyph: OutlinedGlyph,
text_settings: &TextSettings,
) -> Result<GlyphAtlasInfo, TextError> {
if !text_settings.allow_dynamic_font_size {
if self.font_atlases.len() >= text_settings.max_font_atlases.get() {
return Err(TextError::ExceedMaxTextAtlases(
text_settings.max_font_atlases.get(),
));
}
} else {
// Clear last space in queue to make room for new font size
while self.queue.len() >= text_settings.max_font_atlases.get() - 1 {
if let Some(font_size_key) = self.queue.pop() {
self.font_atlases.remove(&font_size_key);
}
}
}
let glyph = outlined_glyph.glyph();
let glyph_id = glyph.id;
let glyph_position = glyph.position;
let font_size = glyph.scale.y;
let font_atlases = self
.font_atlases
.entry(FloatOrd(font_size))
.or_insert_with(|| {
vec![FontAtlas::new(
textures,
texture_atlases,
Vec2::splat(512.0),
)]
});
self.queue.insert(0, FloatOrd(font_size));
let glyph_texture = Font::get_outlined_glyph_texture(outlined_glyph);
let add_char_to_font_atlas = |atlas: &mut FontAtlas| -> bool {
atlas.add_glyph(
textures,
texture_atlases,
glyph_id,
glyph_position.into(),
&glyph_texture,
)
};
if !font_atlases.iter_mut().any(add_char_to_font_atlas) {
// Find the largest dimension of the glyph, either its width or its height
let glyph_max_size: u32 = glyph_texture
.texture_descriptor
.size
.height
.max(glyph_texture.texture_descriptor.size.width);
// Pick the higher of 512 or the smallest power of 2 greater than glyph_max_size
let containing = (1u32 << (32 - glyph_max_size.leading_zeros())).max(512) as f32;
font_atlases.push(FontAtlas::new(
textures,
texture_atlases,
Vec2::new(containing, containing),
));
if !font_atlases.last_mut().unwrap().add_glyph(
textures,
texture_atlases,
glyph_id,
glyph_position.into(),
&glyph_texture,
) {
return Err(TextError::FailedToAddGlyph(glyph_id));
}
}
Ok(self
.get_glyph_atlas_info(font_size, glyph_id, glyph_position)
.unwrap())
}
pub fn get_glyph_atlas_info(
&mut self,
font_size: f32,
glyph_id: GlyphId,
position: Point,
) -> Option<GlyphAtlasInfo> {
// Move to front of used queue.
let some_index = self.queue.iter().position(|x| *x == FloatOrd(font_size));
if let Some(index) = some_index {
let key = self.queue.remove(index);
self.queue.insert(0, key);
}
self.font_atlases
.get(&FloatOrd(font_size))
.and_then(|font_atlases| {
font_atlases
.iter()
.find_map(|atlas| {
atlas
.get_glyph_index(glyph_id, position.into())
.map(|glyph_index| (glyph_index, atlas.texture_atlas.clone_weak()))
})
.map(|(glyph_index, texture_atlas)| GlyphAtlasInfo {
texture_atlas,
glyph_index,
})
})
}
}