hephae_text/
atlas.rs

1//! Defines font atlas sets for rendering glyphs.
2
3use bevy_asset::{RenderAssetUsages, prelude::*};
4use bevy_ecs::prelude::*;
5use bevy_image::prelude::*;
6use bevy_math::{ivec2, prelude::*, uvec2};
7use bevy_reflect::prelude::*;
8use bevy_render::{
9    Extract,
10    render_resource::{Extent3d, TextureDimension, TextureFormat},
11};
12use bevy_utils::HashMap;
13use cosmic_text::{CacheKey, FontSystem, LayoutGlyph, Placement, SwashCache, SwashContent};
14use guillotiere::{AtlasAllocator, size2};
15
16use crate::layout::FontLayoutError;
17
18#[derive(Default)]
19pub(crate) struct FontAtlases(HashMap<FontAtlasKey, Handle<FontAtlas>>);
20impl FontAtlases {
21    #[inline]
22    pub fn atlas_mut<'a>(
23        &mut self,
24        key: FontAtlasKey,
25        atlases: &'a mut Assets<FontAtlas>,
26    ) -> (AssetId<FontAtlas>, &'a mut FontAtlas) {
27        let id = self
28            .0
29            .entry(key)
30            .or_insert_with(|| {
31                atlases.add(FontAtlas {
32                    key,
33                    alloc: AtlasAllocator::new(size2(512, 512)),
34                    image: Handle::Weak(AssetId::invalid()),
35                    map: HashMap::new(),
36                    nodes: Vec::new(),
37                })
38            })
39            .id();
40
41        (id, atlases.get_mut(id).unwrap())
42    }
43}
44
45#[derive(Copy, Clone, Hash, Eq, PartialEq)]
46pub(crate) struct FontAtlasKey {
47    pub font_size: u32,
48    pub antialias: bool,
49}
50
51/// A texture atlas dynamically containing a font's glyphs.
52#[derive(Asset, TypePath)]
53pub struct FontAtlas {
54    key: FontAtlasKey,
55    alloc: AtlasAllocator,
56    image: Handle<Image>,
57    map: HashMap<CacheKey, usize>,
58    nodes: Vec<(IVec2, URect)>,
59}
60
61impl FontAtlas {
62    /// Gets the underlying images of this font atlas.
63    #[inline]
64    pub fn image(&self) -> AssetId<Image> {
65        self.image.id()
66    }
67
68    /// Gets the size of this font atlas.
69    #[inline]
70    pub fn size(&self) -> UVec2 {
71        let size = self.alloc.size().cast::<u32>();
72        uvec2(size.width, size.height)
73    }
74
75    /// Gets the glyph information in a tuple of positional offset, size, and index.
76    #[inline]
77    pub fn get_info(&self, glyph: &LayoutGlyph) -> Option<(IVec2, URect, usize)> {
78        self.map.get(&glyph.physical((0., 0.), 1.).cache_key).and_then(|&index| {
79            let (offset, rect) = self.get_info_index(index)?;
80            Some((offset, rect, index))
81        })
82    }
83
84    /// Gets the glyph information based on its index in a tuple of positional offset and size.
85    #[inline]
86    pub fn get_info_index(&self, index: usize) -> Option<(IVec2, URect)> {
87        Some(*self.nodes.get(index)?)
88    }
89
90    /// Gets or loads the glyph information.
91    pub fn get_or_create_info(
92        &mut self,
93        sys: &mut FontSystem,
94        cache: &mut SwashCache,
95        glyph: &LayoutGlyph,
96        images: &mut Assets<Image>,
97    ) -> Result<(IVec2, URect, usize), FontLayoutError> {
98        if let Some(info) = self.get_info(glyph) {
99            return Ok(info);
100        }
101
102        let phys = glyph.physical((0., 0.), 1.);
103        let swash_image = cache
104            .get_image_uncached(sys, phys.cache_key)
105            .ok_or(FontLayoutError::NoGlyphImage(phys.cache_key))?;
106
107        let Placement {
108            left,
109            top,
110            width,
111            height,
112        } = swash_image.placement;
113
114        if width == 0 || height == 0 {
115            self.map.insert(phys.cache_key, self.nodes.len());
116            self.nodes.push((ivec2(left, top), URect::new(0, 0, width, height)));
117
118            Ok((ivec2(left, top), URect::new(0, 0, width, height), self.nodes.len() - 1))
119        } else {
120            loop {
121                match self.alloc.allocate(size2(width as i32 + 2, height as i32 + 2)) {
122                    Some(alloc) => {
123                        let mut rect = alloc.rectangle.cast::<u32>();
124                        rect.min.x += 1;
125                        rect.min.y += 1;
126                        rect.max.x -= 1;
127                        rect.max.y -= 1;
128
129                        let alloc_size = self.alloc.size().cast::<u32>();
130
131                        self.map.insert(phys.cache_key, self.nodes.len());
132                        self.nodes
133                            .push((ivec2(left, top), URect::new(rect.min.x, rect.min.y, rect.max.x, rect.max.y)));
134
135                        let image = match images.get_mut(&self.image) {
136                            Some(image)
137                                if {
138                                    let size = image.texture_descriptor.size;
139                                    size.width == alloc_size.width && size.height == alloc_size.height
140                                } =>
141                            {
142                                image
143                            }
144                            _ => {
145                                let old = images.remove(&self.image);
146                                let new = Image::new(
147                                    Extent3d {
148                                        width: alloc_size.width,
149                                        height: alloc_size.height,
150                                        depth_or_array_layers: 1,
151                                    },
152                                    TextureDimension::D2,
153                                    match old {
154                                        Some(old) => {
155                                            let old_size = old.texture_descriptor.size;
156                                            let mut data = Vec::with_capacity(
157                                                alloc_size.width as usize * alloc_size.height as usize * 4,
158                                            );
159
160                                            let copy_amount = (old_size.width.min(alloc_size.width) * 4) as usize;
161                                            let copy_left =
162                                                alloc_size.width.saturating_sub(old_size.width.min(alloc_size.width));
163
164                                            for y in 0..old_size.height as usize {
165                                                data.extend_from_slice(
166                                                    &old.data[(y * copy_amount)..((y + 1) * copy_amount)],
167                                                );
168                                                for _ in 0..copy_left {
169                                                    data.extend_from_slice(&[0, 0, 0, 0]);
170                                                }
171                                            }
172
173                                            data
174                                        }
175                                        None => vec![0; alloc_size.width as usize * alloc_size.height as usize * 4],
176                                    },
177                                    TextureFormat::Rgba8UnormSrgb,
178                                    RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
179                                );
180
181                                self.image = images.add(new);
182                                images.get_mut(&self.image).unwrap()
183                            }
184                        };
185
186                        let row = image.texture_descriptor.size.width as usize * 4;
187                        let from_x = rect.min.x as usize;
188                        let to_x = rect.max.x as usize;
189                        let src_row = to_x - from_x;
190
191                        let alpha = |a| match self.key.antialias {
192                            false => {
193                                if a > 127 {
194                                    255
195                                } else {
196                                    0
197                                }
198                            }
199                            true => a,
200                        };
201
202                        for (src_y, dst_y) in (rect.min.y as usize..rect.max.y as usize).enumerate() {
203                            for (src_x, dst_x) in (from_x..to_x).enumerate() {
204                                image.data[dst_y * row + dst_x * 4..dst_y * row + dst_x * 4 + 4].copy_from_slice(
205                                    &match swash_image.content {
206                                        SwashContent::Mask => {
207                                            [255, 255, 255, alpha(swash_image.data[src_y * src_row + src_x])]
208                                        }
209                                        SwashContent::Color => {
210                                            let data = &swash_image.data
211                                                [src_y * src_row * 4 + src_x * 4..src_y * src_row * 4 + src_x * 4 + 4];
212                                            [data[0], data[1], data[2], alpha(data[3])]
213                                        }
214                                        SwashContent::SubpixelMask => {
215                                            unimplemented!("sub-pixel antialiasing is unimplemented")
216                                        }
217                                    },
218                                );
219                            }
220                        }
221
222                        break Ok((
223                            ivec2(left, top),
224                            URect::new(rect.min.x, rect.min.y, rect.max.x, rect.max.y),
225                            self.nodes.len() - 1,
226                        ));
227                    }
228                    None => self.alloc.grow(self.alloc.size() * 2),
229                }
230            }
231        }
232    }
233}
234
235/// Extracted glyph information for use in rendering.
236#[derive(Resource, Default)]
237pub struct ExtractedFontAtlases(HashMap<AssetId<FontAtlas>, ExtractedFontAtlas>);
238impl ExtractedFontAtlases {
239    /// Gets a [`ExtractedFontAtlas`] based on its asset ID.
240    #[inline]
241    pub fn get(&self, id: impl Into<AssetId<FontAtlas>>) -> Option<&ExtractedFontAtlas> {
242        self.0.get(&id.into())
243    }
244}
245
246/// Extracted glyph information for use in rendering.
247#[derive(Default)]
248pub struct ExtractedFontAtlas {
249    image: AssetId<Image>,
250    size: UVec2,
251    nodes: Vec<(IVec2, URect)>,
252}
253
254impl ExtractedFontAtlas {
255    /// Gets the underlying images of this font atlas.
256    #[inline]
257    pub fn image(&self) -> AssetId<Image> {
258        self.image
259    }
260
261    /// Gets the size of this font atlas.
262    #[inline]
263    pub fn size(&self) -> UVec2 {
264        self.size
265    }
266
267    /// Gets the glyph information based on its index in a tuple of positional offset and size.
268    #[inline]
269    pub fn get_info_index(&self, index: usize) -> Option<(IVec2, URect)> {
270        Some(*self.nodes.get(index)?)
271    }
272}
273
274/// Extracts font atlases into the render world.
275pub fn extract_font_atlases(
276    mut extracted: ResMut<ExtractedFontAtlases>,
277    atlases: Extract<Res<Assets<FontAtlas>>>,
278    mut atlas_events: Extract<EventReader<AssetEvent<FontAtlas>>>,
279) {
280    for (id, atlas) in atlases.iter() {
281        let dst = extracted.0.entry(id).or_default();
282        dst.image = atlas.image.id();
283        dst.size = atlas.size();
284        dst.nodes.clear();
285        dst.nodes.extend(&atlas.nodes);
286    }
287
288    for &e in atlas_events.read() {
289        match e {
290            AssetEvent::Added { .. } | AssetEvent::Modified { .. } | AssetEvent::LoadedWithDependencies { .. } => {}
291            AssetEvent::Removed { id } | AssetEvent::Unused { id } => {
292                extracted.0.remove(&id);
293            }
294        }
295    }
296}