Speed up SDF generation

Using Rayon shaves off ~10% off of gallery.slint on my M3.
This commit is contained in:
Simon Hausmann 2024-10-22 17:30:30 +02:00 committed by Olivier Goffart
parent d8ab4e4600
commit 0ebcbbecbe
4 changed files with 35 additions and 32 deletions

View file

@ -159,7 +159,7 @@ ttf-parser = { version = "0.21" }
# web-sys needs to be >= 0.3.72 for set_fill_style_str
web-sys = { version = "0.3.72", default-features = false }
smol_str = { version = "0.3.1" }
rayon = { version = "1.10.0", default-features = false }
raw-window-handle-06 = { package = "raw-window-handle", version = "0.6", features = ["alloc"] }
[profile.release]

View file

@ -30,7 +30,7 @@ display-diagnostics = ["codemap", "codemap-diagnostic"]
# Enabled the support to render images and font in the binary
software-renderer = ["image", "dep:resvg", "fontdue", "i-slint-common/shared-fontdb"]
embed-glyphs-as-sdf = ["dep:fdsm", "dep:ttf-parser-fdsm", "dep:nalgebra", "dep:image-fdsm"]
embed-glyphs-as-sdf = ["dep:fdsm", "dep:ttf-parser-fdsm", "dep:nalgebra", "dep:image-fdsm", "dep:rayon"]
default = []
@ -64,10 +64,11 @@ fdsm = { version = "0.6.0", optional = true, features = ["ttf-parser"]}
ttf-parser-fdsm = { package = "ttf-parser", version = "0.24.1", optional = true }
image-fdsm = { package = "image", version = "0.25", optional = true, default-features = false }
nalgebra = { version = "0.33.0", optional = true }
rayon = { workspace = true, optional = true }
[dev-dependencies]
i-slint-parser-test-macro = { path = "./parser-test-macro" }
regex = "1.3.7"
spin_on = { workspace = true }
rayon = "1.5.3"
rayon = { workspace = true }

View file

@ -83,7 +83,7 @@ pub type FontCache = Rc<
RefCell<
std::collections::HashMap<
i_slint_common::sharedfontdb::fontdb::ID,
fontdue::FontResult<(Rc<fontdue::Font>, Arc<dyn AsRef<[u8]> + Send + Sync>, u32)>,
fontdue::FontResult<(Arc<fontdue::Font>, Arc<dyn AsRef<[u8]> + Send + Sync>, u32)>,
>,
>,
>;
@ -227,7 +227,7 @@ impl CompilerConfiguration {
fn load_font_by_id(
&self,
face_id: i_slint_common::sharedfontdb::fontdb::ID,
) -> fontdue::FontResult<(Rc<fontdue::Font>, Arc<dyn AsRef<[u8]> + Send + Sync>, u32)> {
) -> fontdue::FontResult<(Arc<fontdue::Font>, Arc<dyn AsRef<[u8]> + Send + Sync>, u32)> {
self.font_cache
.borrow_mut()
.entry(face_id)
@ -246,7 +246,7 @@ impl CompilerConfiguration {
)
.map(|fontdue_font| {
(
Rc::new(fontdue_font),
Arc::new(fontdue_font),
Arc::new(font_data.to_vec())
as Arc<dyn AsRef<[u8]> + Send + Sync>,
face_index,

View file

@ -20,7 +20,7 @@ use i_slint_common::sharedfontdb::{self, fontdb};
struct Font {
id: fontdb::ID,
#[deref]
fontdue_font: Rc<fontdue::Font>,
fontdue_font: Arc<fontdue::Font>,
face_data: Arc<dyn AsRef<[u8]> + Send + Sync>,
face_index: u32,
}
@ -442,6 +442,8 @@ fn embed_sdf_glyphs(
font: &Font,
fallback_fonts: &[Font],
) -> Vec<BitmapGlyphs> {
use rayon::prelude::*;
const RANGE: f64 = 6.;
let Some(max_size) = pixel_sizes.iter().max() else {
@ -450,31 +452,31 @@ fn embed_sdf_glyphs(
let min_size = pixel_sizes.iter().min().expect("we have a 'max' so the vector is not empty");
let target_pixel_size = (max_size * 2 / 3).max(12).min(RANGE as i16 * min_size);
let mut glyph_data = Vec::new();
glyph_data.resize(character_map.len(), Default::default());
for CharacterMapEntry { code_point, glyph_index } in character_map {
let glyph = core::iter::once(font)
.chain(fallback_fonts.iter())
.find_map(|font| {
(font.lookup_glyph_index(*code_point) != 0)
.then(|| generate_sdf_for_glyph(font, *code_point, target_pixel_size, RANGE))
})
.unwrap_or_else(|| generate_sdf_for_glyph(font, *code_point, target_pixel_size, RANGE))
.map(|(metrics, bitmap)| BitmapGlyph {
x: i16::try_from(metrics.xmin).expect("large glyph x coordinate"),
y: i16::try_from(metrics.ymin).expect("large glyph y coordinate"),
width: i16::try_from(metrics.width).expect("large width"),
height: i16::try_from(metrics.height).expect("large height"),
x_advance: i16::try_from(metrics.advance_width as i64)
.expect("large advance width"),
data: bitmap,
})
.unwrap_or_default();
glyph_data[*glyph_index as usize] = glyph;
}
let glyph_data = character_map
.par_iter()
.map(|CharacterMapEntry { code_point, .. }| {
core::iter::once(font)
.chain(fallback_fonts.iter())
.find_map(|font| {
(font.lookup_glyph_index(*code_point) != 0).then(|| {
generate_sdf_for_glyph(font, *code_point, target_pixel_size, RANGE)
})
})
.unwrap_or_else(|| {
generate_sdf_for_glyph(font, *code_point, target_pixel_size, RANGE)
})
.map(|(metrics, bitmap)| BitmapGlyph {
x: i16::try_from(metrics.xmin).expect("large glyph x coordinate"),
y: i16::try_from(metrics.ymin).expect("large glyph y coordinate"),
width: i16::try_from(metrics.width).expect("large width"),
height: i16::try_from(metrics.height).expect("large height"),
x_advance: i16::try_from(metrics.advance_width as i64)
.expect("large advance width"),
data: bitmap,
})
.unwrap_or_default()
})
.collect::<Vec<_>>();
vec![BitmapGlyphs { pixel_size: target_pixel_size, glyph_data }]
}