1use bevy_asset::{Assets, Handle};
2use bevy_image::{prelude::*, ImageSampler};
3use bevy_math::{IVec2, UVec2};
4use bevy_platform::collections::HashMap;
5use bevy_render::{
6 render_asset::RenderAssetUsages,
7 render_resource::{Extent3d, TextureDimension, TextureFormat},
8};
9
10use crate::{FontSmoothing, GlyphAtlasLocation, TextError};
11
12pub struct FontAtlas {
25 pub dynamic_texture_atlas_builder: DynamicTextureAtlasBuilder,
27 pub glyph_to_atlas_index: HashMap<cosmic_text::CacheKey, GlyphAtlasLocation>,
29 pub texture_atlas: Handle<TextureAtlasLayout>,
31 pub texture: Handle<Image>,
33}
34
35impl FontAtlas {
36 pub fn new(
38 textures: &mut Assets<Image>,
39 texture_atlases_layout: &mut Assets<TextureAtlasLayout>,
40 size: UVec2,
41 font_smoothing: FontSmoothing,
42 ) -> FontAtlas {
43 let mut image = Image::new_fill(
44 Extent3d {
45 width: size.x,
46 height: size.y,
47 depth_or_array_layers: 1,
48 },
49 TextureDimension::D2,
50 &[0, 0, 0, 0],
51 TextureFormat::Rgba8UnormSrgb,
52 RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
54 );
55 if font_smoothing == FontSmoothing::None {
56 image.sampler = ImageSampler::nearest();
57 }
58 let texture = textures.add(image);
59 let texture_atlas = texture_atlases_layout.add(TextureAtlasLayout::new_empty(size));
60 Self {
61 texture_atlas,
62 glyph_to_atlas_index: HashMap::default(),
63 dynamic_texture_atlas_builder: DynamicTextureAtlasBuilder::new(size, 1),
64 texture,
65 }
66 }
67
68 pub fn get_glyph_index(&self, cache_key: cosmic_text::CacheKey) -> Option<GlyphAtlasLocation> {
70 self.glyph_to_atlas_index.get(&cache_key).copied()
71 }
72
73 pub fn has_glyph(&self, cache_key: cosmic_text::CacheKey) -> bool {
75 self.glyph_to_atlas_index.contains_key(&cache_key)
76 }
77
78 pub fn add_glyph(
90 &mut self,
91 textures: &mut Assets<Image>,
92 atlas_layouts: &mut Assets<TextureAtlasLayout>,
93 cache_key: cosmic_text::CacheKey,
94 texture: &Image,
95 offset: IVec2,
96 ) -> Result<(), TextError> {
97 let atlas_layout = atlas_layouts.get_mut(&self.texture_atlas).unwrap();
98 let atlas_texture = textures.get_mut(&self.texture).unwrap();
99
100 if let Ok(glyph_index) =
101 self.dynamic_texture_atlas_builder
102 .add_texture(atlas_layout, texture, atlas_texture)
103 {
104 self.glyph_to_atlas_index.insert(
105 cache_key,
106 GlyphAtlasLocation {
107 glyph_index,
108 offset,
109 },
110 );
111 Ok(())
112 } else {
113 Err(TextError::FailedToAddGlyph(cache_key.glyph_id))
114 }
115 }
116}
117
118impl core::fmt::Debug for FontAtlas {
119 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120 f.debug_struct("FontAtlas")
121 .field("glyph_to_atlas_index", &self.glyph_to_atlas_index)
122 .field("texture_atlas", &self.texture_atlas)
123 .field("texture", &self.texture)
124 .field("dynamic_texture_atlas_builder", &"[...]")
125 .finish()
126 }
127}