Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_text/src/font_atlas.rs
6596 views
1
use bevy_asset::{Assets, Handle, RenderAssetUsages};
2
use bevy_image::{prelude::*, ImageSampler, ToExtents};
3
use bevy_math::{IVec2, UVec2};
4
use bevy_platform::collections::HashMap;
5
use wgpu_types::{TextureDimension, TextureFormat};
6
7
use crate::{FontSmoothing, GlyphAtlasLocation, TextError};
8
9
/// Rasterized glyphs are cached, stored in, and retrieved from, a `FontAtlas`.
10
///
11
/// A `FontAtlas` contains one or more textures, each of which contains one or more glyphs packed into them.
12
///
13
/// A [`FontAtlasSet`](crate::FontAtlasSet) contains a `FontAtlas` for each font size in the same font face.
14
///
15
/// For the same font face and font size, a glyph will be rasterized differently for different subpixel offsets.
16
/// In practice, ranges of subpixel offsets are grouped into subpixel bins to limit the number of rasterized glyphs,
17
/// providing a trade-off between visual quality and performance.
18
///
19
/// A [`CacheKey`](cosmic_text::CacheKey) encodes all of the information of a subpixel-offset glyph and is used to
20
/// find that glyphs raster in a [`TextureAtlas`] through its corresponding [`GlyphAtlasLocation`].
21
pub struct FontAtlas {
22
/// Used to update the [`TextureAtlasLayout`].
23
pub dynamic_texture_atlas_builder: DynamicTextureAtlasBuilder,
24
/// A mapping between subpixel-offset glyphs and their [`GlyphAtlasLocation`].
25
pub glyph_to_atlas_index: HashMap<cosmic_text::CacheKey, GlyphAtlasLocation>,
26
/// The handle to the [`TextureAtlasLayout`] that holds the rasterized glyphs.
27
pub texture_atlas: Handle<TextureAtlasLayout>,
28
/// The texture where this font atlas is located
29
pub texture: Handle<Image>,
30
}
31
32
impl FontAtlas {
33
/// Create a new [`FontAtlas`] with the given size, adding it to the appropriate asset collections.
34
pub fn new(
35
textures: &mut Assets<Image>,
36
texture_atlases_layout: &mut Assets<TextureAtlasLayout>,
37
size: UVec2,
38
font_smoothing: FontSmoothing,
39
) -> FontAtlas {
40
let mut image = Image::new_fill(
41
size.to_extents(),
42
TextureDimension::D2,
43
&[0, 0, 0, 0],
44
TextureFormat::Rgba8UnormSrgb,
45
// Need to keep this image CPU persistent in order to add additional glyphs later on
46
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
47
);
48
if font_smoothing == FontSmoothing::None {
49
image.sampler = ImageSampler::nearest();
50
}
51
let texture = textures.add(image);
52
let texture_atlas = texture_atlases_layout.add(TextureAtlasLayout::new_empty(size));
53
Self {
54
texture_atlas,
55
glyph_to_atlas_index: HashMap::default(),
56
dynamic_texture_atlas_builder: DynamicTextureAtlasBuilder::new(size, 1),
57
texture,
58
}
59
}
60
61
/// Get the [`GlyphAtlasLocation`] for a subpixel-offset glyph.
62
pub fn get_glyph_index(&self, cache_key: cosmic_text::CacheKey) -> Option<GlyphAtlasLocation> {
63
self.glyph_to_atlas_index.get(&cache_key).copied()
64
}
65
66
/// Checks if the given subpixel-offset glyph is contained in this [`FontAtlas`].
67
pub fn has_glyph(&self, cache_key: cosmic_text::CacheKey) -> bool {
68
self.glyph_to_atlas_index.contains_key(&cache_key)
69
}
70
71
/// Add a glyph to the atlas, updating both its texture and layout.
72
///
73
/// The glyph is represented by `glyph`, and its image content is `glyph_texture`.
74
/// This content is copied into the atlas texture, and the atlas layout is updated
75
/// to store the location of that glyph into the atlas.
76
///
77
/// # Returns
78
///
79
/// Returns `()` if the glyph is successfully added, or [`TextError::FailedToAddGlyph`] otherwise.
80
/// In that case, neither the atlas texture nor the atlas layout are
81
/// modified.
82
pub fn add_glyph(
83
&mut self,
84
textures: &mut Assets<Image>,
85
atlas_layouts: &mut Assets<TextureAtlasLayout>,
86
cache_key: cosmic_text::CacheKey,
87
texture: &Image,
88
offset: IVec2,
89
) -> Result<(), TextError> {
90
let atlas_layout = atlas_layouts.get_mut(&self.texture_atlas).unwrap();
91
let atlas_texture = textures.get_mut(&self.texture).unwrap();
92
93
if let Ok(glyph_index) =
94
self.dynamic_texture_atlas_builder
95
.add_texture(atlas_layout, texture, atlas_texture)
96
{
97
self.glyph_to_atlas_index.insert(
98
cache_key,
99
GlyphAtlasLocation {
100
glyph_index,
101
offset,
102
},
103
);
104
Ok(())
105
} else {
106
Err(TextError::FailedToAddGlyph(cache_key.glyph_id))
107
}
108
}
109
}
110
111
impl core::fmt::Debug for FontAtlas {
112
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113
f.debug_struct("FontAtlas")
114
.field("glyph_to_atlas_index", &self.glyph_to_atlas_index)
115
.field("texture_atlas", &self.texture_atlas)
116
.field("texture", &self.texture)
117
.field("dynamic_texture_atlas_builder", &"[...]")
118
.finish()
119
}
120
}
121
122