diff options
author | 2023-03-19 14:52:30 +0100 | |
---|---|---|
committer | 2023-03-19 14:52:30 +0100 | |
commit | 5f9e7f6cb99467363d691086cb697b2390793afd (patch) | |
tree | e78ffc7af0a5fbb9cf508bcff27402d555f7ab15 /wgpu/src/text.rs | |
parent | d1dc62ebcdab6ec57605d276a02d6dae1e97c30d (diff) | |
download | iced-5f9e7f6cb99467363d691086cb697b2390793afd.tar.gz iced-5f9e7f6cb99467363d691086cb697b2390793afd.tar.bz2 iced-5f9e7f6cb99467363d691086cb697b2390793afd.zip |
Update `cosmic-text` to latest :tada:
Diffstat (limited to 'wgpu/src/text.rs')
-rw-r--r-- | wgpu/src/text.rs | 372 |
1 files changed, 165 insertions, 207 deletions
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 35f24cd9..ac116f69 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -12,23 +12,12 @@ use std::sync::Arc; #[allow(missing_debug_implementations)] pub struct Pipeline { - system: Option<System>, + font_system: RefCell<glyphon::FontSystem>, renderers: Vec<glyphon::TextRenderer>, atlas: glyphon::TextAtlas, prepare_layer: usize, -} - -#[ouroboros::self_referencing] -struct System { - fonts: glyphon::FontSystem, - - #[borrows(fonts)] - #[not_covariant] - measurement_cache: RefCell<Cache<'this>>, - - #[borrows(fonts)] - #[not_covariant] - render_cache: Cache<'this>, + measurement_cache: RefCell<Cache>, + render_cache: Cache, } impl Pipeline { @@ -38,42 +27,23 @@ impl Pipeline { format: wgpu::TextureFormat, ) -> Self { Pipeline { - system: Some( - SystemBuilder { - fonts: glyphon::FontSystem::new_with_fonts( - [glyphon::fontdb::Source::Binary(Arc::new( - include_bytes!("../fonts/Iced-Icons.ttf") - .as_slice(), - ))] - .into_iter(), - ), - measurement_cache_builder: |_| RefCell::new(Cache::new()), - render_cache_builder: |_| Cache::new(), - } - .build(), - ), + font_system: RefCell::new(glyphon::FontSystem::new_with_fonts( + [glyphon::fontdb::Source::Binary(Arc::new( + include_bytes!("../fonts/Iced-Icons.ttf").as_slice(), + ))] + .into_iter(), + )), renderers: Vec::new(), atlas: glyphon::TextAtlas::new(device, queue, format), prepare_layer: 0, + measurement_cache: RefCell::new(Cache::new()), + render_cache: Cache::new(), } } pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { - let heads = self.system.take().unwrap().into_heads(); - - let (locale, mut db) = heads.fonts.into_locale_and_db(); - - db.load_font_source(glyphon::fontdb::Source::Binary(Arc::new( - bytes.into_owned(), - ))); - - self.system = Some( - SystemBuilder { - fonts: glyphon::FontSystem::new_with_locale_and_db(locale, db), - measurement_cache_builder: |_| RefCell::new(Cache::new()), - render_cache_builder: |_| Cache::new(), - } - .build(), + self.font_system.get_mut().db_mut().load_font_source( + glyphon::fontdb::Source::Binary(Arc::new(bytes.into_owned())), ); } @@ -86,126 +56,123 @@ impl Pipeline { scale_factor: f32, target_size: Size<u32>, ) -> bool { - self.system.as_mut().unwrap().with_mut(|fields| { - if self.renderers.len() <= self.prepare_layer { - self.renderers - .push(glyphon::TextRenderer::new(device, queue)); - } + if self.renderers.len() <= self.prepare_layer { + self.renderers + .push(glyphon::TextRenderer::new(device, queue)); + } - let renderer = &mut self.renderers[self.prepare_layer]; - - let keys: Vec<_> = sections - .iter() - .map(|section| { - let (key, _) = fields.render_cache.allocate( - fields.fonts, - Key { - content: section.content, - size: section.size * scale_factor, - font: section.font, - bounds: Size { - width: (section.bounds.width * scale_factor) - .ceil(), - height: (section.bounds.height * scale_factor) - .ceil(), - }, + let font_system = self.font_system.get_mut(); + let renderer = &mut self.renderers[self.prepare_layer]; + + let keys: Vec<_> = sections + .iter() + .map(|section| { + let (key, _) = self.render_cache.allocate( + font_system, + Key { + content: section.content, + size: section.size * scale_factor, + font: section.font, + bounds: Size { + width: (section.bounds.width * scale_factor).ceil(), + height: (section.bounds.height * scale_factor) + .ceil(), }, - ); - - key - }) - .collect(); - - let bounds = glyphon::TextBounds { - left: (bounds.x * scale_factor) as i32, - top: (bounds.y * scale_factor) as i32, - right: ((bounds.x + bounds.width) * scale_factor) as i32, - bottom: ((bounds.y + bounds.height) * scale_factor) as i32, - }; - - let text_areas = - sections.iter().zip(keys.iter()).map(|(section, key)| { - let buffer = fields - .render_cache - .get(key) - .expect("Get cached buffer"); - - let x = section.bounds.x * scale_factor; - let y = section.bounds.y * scale_factor; - - let (total_lines, max_width) = buffer - .layout_runs() - .enumerate() - .fold((0, 0.0), |(_, max), (i, buffer)| { - (i + 1, buffer.line_w.max(max)) - }); - - let total_height = - total_lines as f32 * section.size * 1.2 * scale_factor; - - let left = match section.horizontal_alignment { - alignment::Horizontal::Left => x, - alignment::Horizontal::Center => x - max_width / 2.0, - alignment::Horizontal::Right => x - max_width, - }; - - let top = match section.vertical_alignment { - alignment::Vertical::Top => y, - alignment::Vertical::Center => y - total_height / 2.0, - alignment::Vertical::Bottom => y - total_height, - }; - - glyphon::TextArea { - buffer, - left: left as i32, - top: top as i32, - bounds, - default_color: { - let [r, g, b, a] = section.color.into_linear(); - - glyphon::Color::rgba( - (r * 255.0) as u8, - (g * 255.0) as u8, - (b * 255.0) as u8, - (a * 255.0) as u8, - ) - }, - } - }); - - let result = renderer.prepare( - device, - queue, - &mut self.atlas, - glyphon::Resolution { - width: target_size.width, - height: target_size.height, - }, - text_areas, - &mut glyphon::SwashCache::new(fields.fonts), - ); + }, + ); + + key + }) + .collect(); + + let bounds = glyphon::TextBounds { + left: (bounds.x * scale_factor) as i32, + top: (bounds.y * scale_factor) as i32, + right: ((bounds.x + bounds.width) * scale_factor) as i32, + bottom: ((bounds.y + bounds.height) * scale_factor) as i32, + }; - match result { - Ok(()) => { - self.prepare_layer += 1; + let text_areas = + sections.iter().zip(keys.iter()).map(|(section, key)| { + let buffer = + self.render_cache.get(key).expect("Get cached buffer"); + + let x = section.bounds.x * scale_factor; + let y = section.bounds.y * scale_factor; + + let (total_lines, max_width) = buffer + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); + + let total_height = + total_lines as f32 * section.size * 1.2 * scale_factor; + + let left = match section.horizontal_alignment { + alignment::Horizontal::Left => x, + alignment::Horizontal::Center => x - max_width / 2.0, + alignment::Horizontal::Right => x - max_width, + }; + + let top = match section.vertical_alignment { + alignment::Vertical::Top => y, + alignment::Vertical::Center => y - total_height / 2.0, + alignment::Vertical::Bottom => y - total_height, + }; + + glyphon::TextArea { + buffer, + left: left as i32, + top: top as i32, + bounds, + default_color: { + let [r, g, b, a] = section.color.into_linear(); + + glyphon::Color::rgba( + (r * 255.0) as u8, + (g * 255.0) as u8, + (b * 255.0) as u8, + (a * 255.0) as u8, + ) + }, + } + }); + + let result = renderer.prepare( + device, + queue, + font_system, + &mut self.atlas, + glyphon::Resolution { + width: target_size.width, + height: target_size.height, + }, + text_areas, + &mut glyphon::SwashCache::new(), + ); + + match result { + Ok(()) => { + self.prepare_layer += 1; + true + } + Err(glyphon::PrepareError::AtlasFull(content_type)) => { + self.prepare_layer = 0; + + #[allow(clippy::needless_bool)] + if self.atlas.grow(device, content_type) { + false + } else { + // If the atlas cannot grow, then all bets are off. + // Instead of panicking, we will just pray that the result + // will be somewhat readable... true } - Err(glyphon::PrepareError::AtlasFull(content_type)) => { - self.prepare_layer = 0; - - #[allow(clippy::needless_bool)] - if self.atlas.grow(device, content_type) { - false - } else { - // If the atlas cannot grow, then all bets are off. - // Instead of panicking, we will just pray that the result - // will be somewhat readable... - true - } - } } - }) + } } pub fn render<'a>( @@ -230,11 +197,7 @@ impl Pipeline { pub fn end_frame(&mut self) { self.atlas.trim(); - - self.system - .as_mut() - .unwrap() - .with_render_cache_mut(|cache| cache.trim()); + self.render_cache.trim(); self.prepare_layer = 0; } @@ -246,28 +209,26 @@ impl Pipeline { font: Font, bounds: Size, ) -> (f32, f32) { - self.system.as_ref().unwrap().with(|fields| { - let mut measurement_cache = fields.measurement_cache.borrow_mut(); - - let (_, paragraph) = measurement_cache.allocate( - fields.fonts, - Key { - content, - size, - font, - bounds, - }, - ); + let mut measurement_cache = self.measurement_cache.borrow_mut(); + + let (_, paragraph) = measurement_cache.allocate( + &mut self.font_system.borrow_mut(), + Key { + content, + size, + font, + bounds, + }, + ); - let (total_lines, max_width) = paragraph - .layout_runs() - .enumerate() - .fold((0, 0.0), |(_, max), (i, buffer)| { - (i + 1, buffer.line_w.max(max)) - }); + let (total_lines, max_width) = paragraph + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); - (max_width, size * 1.2 * total_lines as f32) - }) + (max_width, size * 1.2 * total_lines as f32) } pub fn hit_test( @@ -279,30 +240,25 @@ impl Pipeline { point: Point, _nearest_only: bool, ) -> Option<Hit> { - self.system.as_ref().unwrap().with(|fields| { - let mut measurement_cache = fields.measurement_cache.borrow_mut(); - - let (_, paragraph) = measurement_cache.allocate( - fields.fonts, - Key { - content, - size, - font, - bounds, - }, - ); + let mut measurement_cache = self.measurement_cache.borrow_mut(); + + let (_, paragraph) = measurement_cache.allocate( + &mut self.font_system.borrow_mut(), + Key { + content, + size, + font, + bounds, + }, + ); - let cursor = paragraph.hit(point.x, point.y)?; + let cursor = paragraph.hit(point.x, point.y)?; - Some(Hit::CharOffset(cursor.index)) - }) + Some(Hit::CharOffset(cursor.index)) } pub fn trim_measurement_cache(&mut self) { - self.system - .as_mut() - .unwrap() - .with_measurement_cache_mut(|cache| cache.borrow_mut().trim()); + self.measurement_cache.borrow_mut().trim(); } } @@ -317,8 +273,8 @@ fn to_family(font: Font) -> glyphon::Family<'static> { } } -struct Cache<'a> { - entries: FxHashMap<KeyHash, glyphon::Buffer<'a>>, +struct Cache { + entries: FxHashMap<KeyHash, glyphon::Buffer>, recently_used: FxHashSet<KeyHash>, hasher: HashBuilder, } @@ -329,7 +285,7 @@ type HashBuilder = twox_hash::RandomXxHashBuilder64; #[cfg(target_arch = "wasm32")] type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>; -impl<'a> Cache<'a> { +impl Cache { fn new() -> Self { Self { entries: FxHashMap::default(), @@ -338,15 +294,15 @@ impl<'a> Cache<'a> { } } - fn get(&self, key: &KeyHash) -> Option<&glyphon::Buffer<'a>> { + fn get(&self, key: &KeyHash) -> Option<&glyphon::Buffer> { self.entries.get(key) } fn allocate( &mut self, - fonts: &'a glyphon::FontSystem, + font_system: &mut glyphon::FontSystem, key: Key<'_>, - ) -> (KeyHash, &mut glyphon::Buffer<'a>) { + ) -> (KeyHash, &mut glyphon::Buffer) { let hash = { let mut hasher = self.hasher.build_hasher(); @@ -361,13 +317,15 @@ impl<'a> Cache<'a> { if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) { let metrics = glyphon::Metrics::new(key.size, key.size * 1.2); - let mut buffer = glyphon::Buffer::new(fonts, metrics); + let mut buffer = glyphon::Buffer::new(font_system, metrics); buffer.set_size( + font_system, key.bounds.width, key.bounds.height.max(key.size * 1.2), ); buffer.set_text( + font_system, key.content, glyphon::Attrs::new() .family(to_family(key.font)) |