this repo has no description
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

🐛Fix text not rendered

Load fonts

authored by

Gwenn Le Bihan and committed by
Gwen Le Bihan
d559d196 782fa97a

+115 -13
+3
.mailmap
··· 1 + Gwenn Le Bihan <gwenn.lebihan7@gmail.com> Ewen Le Bihan 2 + Gwenn Le Bihan <gwenn.lebihan7@gmail.com> Gwen Le Bihan 3 + Gwenn Le Bihan <gwenn.lebihan7@gmail.com> Gwenn Le Bihan
+45 -7
src/canvas.rs
··· 1 1 use core::panic; 2 2 use rayon::prelude::*; 3 - use std::{collections::HashMap, fs::OpenOptions, io::Write, ops::Range}; 3 + use resvg::usvg; 4 + use std::{collections::HashMap, fs::OpenOptions, io::Write, ops::Range, sync::Arc}; 4 5 5 6 use itertools::Itertools as _; 6 7 use measure_time::info_time; 7 8 use rand::Rng; 8 9 9 10 use crate::{ 10 - layer::Layer, objects::Object, random_color, Angle, Color, ColorMapping, ColoredObject, 11 - Containable, Fill, Filter, LineSegment, ObjectSizes, Point, Region, 11 + fonts::{load_fonts, FontOptions}, 12 + layer::Layer, 13 + objects::Object, 14 + random_color, Angle, Color, ColorMapping, ColoredObject, Containable, Fill, Filter, 15 + LineSegment, ObjectSizes, Point, Region, 12 16 }; 13 17 14 18 #[derive(Debug, Clone)] ··· 19 23 pub polygon_vertices_range: Range<usize>, 20 24 pub canvas_outter_padding: usize, 21 25 pub object_sizes: ObjectSizes, 26 + pub font_options: FontOptions, 22 27 pub colormap: ColorMapping, 28 + 23 29 /// The layers are in order of top to bottom: the first layer will be rendered on top of the second, etc. 24 30 pub layers: Vec<Layer>, 25 31 pub background: Option<Color>, ··· 28 34 29 35 /// Render cache for the SVG string. Prevents having to re-calculate a pixmap when the SVG hasn't changed. 30 36 png_render_cache: Option<String>, 37 + fontdb: Option<Arc<usvg::fontdb::Database>>, 31 38 } 32 39 33 40 impl Canvas { ··· 187 194 polygon_vertices_range: 2..7, 188 195 canvas_outter_padding: 10, 189 196 object_sizes: ObjectSizes::default(), 197 + font_options: FontOptions::default(), 190 198 colormap: ColorMapping::default(), 191 199 layers: vec![], 192 200 world_region: Region::new(0, 0, 3, 3).unwrap(), 193 201 background: None, 194 202 png_render_cache: None, 203 + fontdb: None, 195 204 } 205 + } 206 + 207 + pub fn fonts_loaded(&self) -> bool { 208 + self.fontdb.is_some() 209 + } 210 + 211 + fn load_fonts(&mut self) -> anyhow::Result<()> { 212 + if self.fonts_loaded() { 213 + return Ok(()); 214 + } 215 + 216 + info_time!("load_fonts"); 217 + let usvg = load_fonts(&self.font_options)?; 218 + self.fontdb = Some(usvg.fontdb); 219 + return Ok(()); 196 220 } 197 221 198 222 pub fn random_layer(&self, name: &str) -> Layer { ··· 586 610 height: u32, 587 611 ) -> anyhow::Result<tiny_skia::Pixmap> { 588 612 info_time!("render_to_pixmap_no_cache"); 613 + 614 + self.load_fonts()?; 615 + 589 616 let mut pixmap = self.create_pixmap(width, height); 590 617 591 - let parsed_svg = &svg_to_usvg_tree(&self.render_to_svg()?)?; 618 + let parsed_svg = &svg_to_usvg_tree(&self.render_to_svg()?, &self.fontdb)?; 592 619 593 620 self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg); 594 621 ··· 603 630 ) -> anyhow::Result<Option<tiny_skia::Pixmap>> { 604 631 info_time!("render_to_pixmap"); 605 632 633 + self.load_fonts()?; 634 + 606 635 let new_svg_contents = self.render_to_svg()?; 607 636 if let Some(cached_svg) = &self.png_render_cache { 608 637 if *cached_svg == new_svg_contents { ··· 613 642 614 643 let mut pixmap = self.create_pixmap(width, height); 615 644 616 - let parsed_svg = &svg_to_usvg_tree(&new_svg_contents)?; 645 + let parsed_svg = &svg_to_usvg_tree(&new_svg_contents, &self.fontdb)?; 617 646 618 647 self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg); 619 648 ··· 672 701 } 673 702 } 674 703 675 - fn svg_to_usvg_tree(svg: &str) -> anyhow::Result<resvg::usvg::Tree> { 704 + fn svg_to_usvg_tree( 705 + svg: &str, 706 + fontdb: &Option<Arc<usvg::fontdb::Database>>, 707 + ) -> anyhow::Result<resvg::usvg::Tree> { 676 708 info_time!("svg_to_usvg_tree"); 677 709 Ok(resvg::usvg::Tree::from_str( 678 710 svg, 679 - &resvg::usvg::Options::default(), 711 + &match fontdb { 712 + Some(fontdb) => resvg::usvg::Options { 713 + fontdb: fontdb.clone(), 714 + ..Default::default() 715 + }, 716 + None => resvg::usvg::Options::default(), 717 + }, 680 718 )?) 681 719 }
+45
src/fonts.rs
··· 1 + use std::path::PathBuf; 2 + 3 + use resvg::usvg; 4 + 5 + #[derive(Default, Debug, Clone)] 6 + pub struct FontOptions { 7 + skip_system_fonts: bool, 8 + font_files: Vec<PathBuf>, 9 + font_dirs: Vec<PathBuf>, 10 + serif_family: Option<String>, 11 + sans_serif_family: Option<String>, 12 + cursive_family: Option<String>, 13 + fantasy_family: Option<String>, 14 + monospace_family: Option<String>, 15 + } 16 + 17 + pub fn load_fonts(args: &FontOptions) -> anyhow::Result<usvg::Options> { 18 + let mut usvg = usvg::Options { 19 + font_family: args.sans_serif_family.clone().unwrap_or("Arial".into()), 20 + ..Default::default() 21 + }; 22 + let fontdb = usvg.fontdb_mut(); 23 + 24 + if !args.skip_system_fonts { 25 + fontdb.load_system_fonts(); 26 + } 27 + 28 + for path in &args.font_files { 29 + if let Err(e) = fontdb.load_font_file(path) { 30 + log::warn!("Failed to load '{}' cause {}.", path.display(), e); 31 + } 32 + } 33 + 34 + for path in &args.font_dirs { 35 + fontdb.load_fonts_dir(path); 36 + } 37 + 38 + fontdb.set_serif_family(args.serif_family.as_deref().unwrap_or("Times New Roman")); 39 + fontdb.set_sans_serif_family(args.sans_serif_family.as_deref().unwrap_or("Arial")); 40 + fontdb.set_cursive_family(args.cursive_family.as_deref().unwrap_or("Comic Sans MS")); 41 + fontdb.set_fantasy_family(args.fantasy_family.as_deref().unwrap_or("Impact")); 42 + fontdb.set_monospace_family(args.monospace_family.as_deref().unwrap_or("Courier New")); 43 + 44 + Ok(usvg) 45 + }
+6
src/layer.rs
··· 49 49 self._render_cache = None; 50 50 } 51 51 52 + // Remove all objects. 53 + pub fn clear(&mut self) { 54 + self.objects.clear(); 55 + self.flush(); 56 + } 57 + 52 58 pub fn replace(&mut self, with: Layer) { 53 59 self.objects.clone_from(&with.objects); 54 60 self.flush();
+1
src/lib.rs
··· 19 19 pub mod video; 20 20 pub mod web; 21 21 pub mod vst; 22 + pub mod fonts; 22 23 pub use animation::*; 23 24 use anyhow::Result; 24 25 pub use audio::*;
+15 -6
src/main.rs
··· 46 46 .flag_sync_with 47 47 .expect("Provide MIDI sync file with --sync-with to render a video"), 48 48 ) 49 - .each_beat(&|canvas, ctx| { 50 - canvas.background = Some(if ctx.beat % 2 == 0 { 51 - Color::Black 52 - } else { 53 - Color::White 54 - }); 49 + .each_frame(&|canvas, ctx| { 50 + let center = canvas.world_region.center(); 51 + canvas.root().clear(); 52 + canvas.root().add_object( 53 + "text", 54 + Object::CenteredText( 55 + center, 56 + format!( 57 + "{} #{} beat {}", 58 + ctx.timestamp, ctx.frame, ctx.beat_fractional 59 + ), 60 + 30.0, 61 + ) 62 + .color(Fill::Solid(Color::White)), 63 + ); 55 64 Ok(()) 56 65 }) 57 66 .render(args.arg_file)